[WEB-2357] fix: update and redefine user roles across the platform (#5466)
* chore: removed viewer role * chore: indentation * chore: remove viewer role * chore: handled user permissions in store * chore: updated the migration file * chore: updated user permissions store * chore: removed the owner key * chore: code refactor * chore: code refactor * chore: code refactor * chore: code refactor * chore: code refactor * fix: build error * chore: updated user permissions store and handled the permissions fetch in workspace and project wrappers * chore: package user enum updated * chore: user permission updated * chore: user permission updated * chore: resolved build errors * chore: resolved build error * chore: resolved build errors * chore: computedFn deep map issue resolved * chore: added back migration * chore: added new field in project table * chore: removed member store in users * chore: private project for admins * chore: workspace notification access validation updated * fix: workspace member edit option * fix: project intake permission validation updated * chore: workspace export settings permission updated * chore: guest_view_all_issues added * chore: guest_view_all_issues added * chore: key changed for guest access * chore: added validation for individual issues * chore: changed the dashboard issues count * chore: added new yarn file * chore: modified yarn file * chore: project page permission updated * chore: project page permission updated * chore: member setting ux updated * chore: build error * fix: yarn lock * fix: build error --------- Co-authored-by: gurusainath <gurusainath007@gmail.com> Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
This commit is contained in:
parent
7013a36629
commit
fdcd9a376c
172 changed files with 2057 additions and 1627 deletions
|
|
@ -210,7 +210,7 @@ class InboxIssueAPIEndpoint(BaseAPIView):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Only project members admins and created_by users can access this endpoint
|
# Only project members admins and created_by users can access this endpoint
|
||||||
if project_member.role <= 10 and str(inbox_issue.created_by_id) != str(
|
if project_member.role <= 5 and str(inbox_issue.created_by_id) != str(
|
||||||
request.user.id
|
request.user.id
|
||||||
):
|
):
|
||||||
return Response(
|
return Response(
|
||||||
|
|
@ -244,9 +244,8 @@ class InboxIssueAPIEndpoint(BaseAPIView):
|
||||||
workspace__slug=slug,
|
workspace__slug=slug,
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
)
|
)
|
||||||
# Only allow guests and viewers to edit name and description
|
# Only allow guests to edit name and description
|
||||||
if project_member.role <= 10:
|
if project_member.role <= 5:
|
||||||
# viewers and guests since only viewers and guests
|
|
||||||
issue_data = {
|
issue_data = {
|
||||||
"name": issue_data.get("name", issue.name),
|
"name": issue_data.get("name", issue.name),
|
||||||
"description_html": issue_data.get(
|
"description_html": issue_data.get(
|
||||||
|
|
@ -286,7 +285,7 @@ class InboxIssueAPIEndpoint(BaseAPIView):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Only project admins and members can edit inbox issue attributes
|
# Only project admins and members can edit inbox issue attributes
|
||||||
if project_member.role > 10:
|
if project_member.role > 5:
|
||||||
serializer = InboxIssueSerializer(
|
serializer = InboxIssueSerializer(
|
||||||
inbox_issue, data=request.data, partial=True
|
inbox_issue, data=request.data, partial=True
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ class ProjectMemberAPIEndpoint(BaseAPIView):
|
||||||
workspace_member = WorkspaceMember.objects.create(
|
workspace_member = WorkspaceMember.objects.create(
|
||||||
workspace=workspace,
|
workspace=workspace,
|
||||||
member=user,
|
member=user,
|
||||||
role=request.data.get("role", 10),
|
role=request.data.get("role", 5),
|
||||||
)
|
)
|
||||||
workspace_member.save()
|
workspace_member.save()
|
||||||
|
|
||||||
|
|
@ -142,7 +142,7 @@ class ProjectMemberAPIEndpoint(BaseAPIView):
|
||||||
project_member = ProjectMember.objects.create(
|
project_member = ProjectMember.objects.create(
|
||||||
project=project,
|
project=project,
|
||||||
member=user,
|
member=user,
|
||||||
role=request.data.get("role", 10),
|
role=request.data.get("role", 5),
|
||||||
)
|
)
|
||||||
project_member.save()
|
project_member.save()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ from enum import Enum
|
||||||
class ROLE(Enum):
|
class ROLE(Enum):
|
||||||
ADMIN = 20
|
ADMIN = 20
|
||||||
MEMBER = 15
|
MEMBER = 15
|
||||||
VIEWER = 10
|
|
||||||
GUEST = 5
|
GUEST = 5
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ from plane.db.models import ProjectMember, WorkspaceMember
|
||||||
# Permission Mappings
|
# Permission Mappings
|
||||||
Admin = 20
|
Admin = 20
|
||||||
Member = 15
|
Member = 15
|
||||||
Viewer = 10
|
|
||||||
Guest = 5
|
Guest = 5
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,8 @@ from plane.db.models import WorkspaceMember
|
||||||
|
|
||||||
|
|
||||||
# Permission Mappings
|
# Permission Mappings
|
||||||
Owner = 20
|
Admin = 20
|
||||||
Admin = 15
|
Member = 15
|
||||||
Member = 10
|
|
||||||
Guest = 5
|
Guest = 5
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -31,7 +30,7 @@ class WorkSpaceBasePermission(BasePermission):
|
||||||
return WorkspaceMember.objects.filter(
|
return WorkspaceMember.objects.filter(
|
||||||
member=request.user,
|
member=request.user,
|
||||||
workspace__slug=view.workspace_slug,
|
workspace__slug=view.workspace_slug,
|
||||||
role__in=[Owner, Admin],
|
role__in=[Admin, Member],
|
||||||
is_active=True,
|
is_active=True,
|
||||||
).exists()
|
).exists()
|
||||||
|
|
||||||
|
|
@ -40,7 +39,7 @@ class WorkSpaceBasePermission(BasePermission):
|
||||||
return WorkspaceMember.objects.filter(
|
return WorkspaceMember.objects.filter(
|
||||||
member=request.user,
|
member=request.user,
|
||||||
workspace__slug=view.workspace_slug,
|
workspace__slug=view.workspace_slug,
|
||||||
role=Owner,
|
role=Admin,
|
||||||
is_active=True,
|
is_active=True,
|
||||||
).exists()
|
).exists()
|
||||||
|
|
||||||
|
|
@ -53,7 +52,7 @@ class WorkspaceOwnerPermission(BasePermission):
|
||||||
return WorkspaceMember.objects.filter(
|
return WorkspaceMember.objects.filter(
|
||||||
workspace__slug=view.workspace_slug,
|
workspace__slug=view.workspace_slug,
|
||||||
member=request.user,
|
member=request.user,
|
||||||
role=Owner,
|
role=Admin,
|
||||||
).exists()
|
).exists()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -65,7 +64,7 @@ class WorkSpaceAdminPermission(BasePermission):
|
||||||
return WorkspaceMember.objects.filter(
|
return WorkspaceMember.objects.filter(
|
||||||
member=request.user,
|
member=request.user,
|
||||||
workspace__slug=view.workspace_slug,
|
workspace__slug=view.workspace_slug,
|
||||||
role__in=[Owner, Admin],
|
role__in=[Admin, Member],
|
||||||
is_active=True,
|
is_active=True,
|
||||||
).exists()
|
).exists()
|
||||||
|
|
||||||
|
|
@ -86,7 +85,7 @@ class WorkspaceEntityPermission(BasePermission):
|
||||||
return WorkspaceMember.objects.filter(
|
return WorkspaceMember.objects.filter(
|
||||||
member=request.user,
|
member=request.user,
|
||||||
workspace__slug=view.workspace_slug,
|
workspace__slug=view.workspace_slug,
|
||||||
role__in=[Owner, Admin],
|
role__in=[Admin, Member],
|
||||||
is_active=True,
|
is_active=True,
|
||||||
).exists()
|
).exists()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,11 @@ from plane.app.permissions import allow_permission, ROLE
|
||||||
class AnalyticsEndpoint(BaseAPIView):
|
class AnalyticsEndpoint(BaseAPIView):
|
||||||
|
|
||||||
@allow_permission(
|
@allow_permission(
|
||||||
[ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER], level="WORKSPACE"
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
],
|
||||||
|
level="WORKSPACE",
|
||||||
)
|
)
|
||||||
def get(self, request, slug):
|
def get(self, request, slug):
|
||||||
x_axis = request.GET.get("x_axis", False)
|
x_axis = request.GET.get("x_axis", False)
|
||||||
|
|
@ -203,7 +207,11 @@ class AnalyticViewViewset(BaseViewSet):
|
||||||
class SavedAnalyticEndpoint(BaseAPIView):
|
class SavedAnalyticEndpoint(BaseAPIView):
|
||||||
|
|
||||||
@allow_permission(
|
@allow_permission(
|
||||||
[ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER], level="WORKSPACE"
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
],
|
||||||
|
level="WORKSPACE",
|
||||||
)
|
)
|
||||||
def get(self, request, slug, analytic_id):
|
def get(self, request, slug, analytic_id):
|
||||||
analytic_view = AnalyticView.objects.get(
|
analytic_view = AnalyticView.objects.get(
|
||||||
|
|
@ -236,7 +244,11 @@ class SavedAnalyticEndpoint(BaseAPIView):
|
||||||
class ExportAnalyticsEndpoint(BaseAPIView):
|
class ExportAnalyticsEndpoint(BaseAPIView):
|
||||||
|
|
||||||
@allow_permission(
|
@allow_permission(
|
||||||
[ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER], level="WORKSPACE"
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
],
|
||||||
|
level="WORKSPACE",
|
||||||
)
|
)
|
||||||
def post(self, request, slug):
|
def post(self, request, slug):
|
||||||
x_axis = request.data.get("x_axis", False)
|
x_axis = request.data.get("x_axis", False)
|
||||||
|
|
@ -302,9 +314,7 @@ class ExportAnalyticsEndpoint(BaseAPIView):
|
||||||
|
|
||||||
class DefaultAnalyticsEndpoint(BaseAPIView):
|
class DefaultAnalyticsEndpoint(BaseAPIView):
|
||||||
|
|
||||||
@allow_permission(
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE")
|
||||||
[ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST], level="WORKSPACE"
|
|
||||||
)
|
|
||||||
def get(self, request, slug):
|
def get(self, request, slug):
|
||||||
filters = issue_filters(request.GET, "GET")
|
filters = issue_filters(request.GET, "GET")
|
||||||
base_issues = Issue.issue_objects.filter(
|
base_issues = Issue.issue_objects.filter(
|
||||||
|
|
|
||||||
|
|
@ -288,7 +288,12 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView):
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER])
|
@allow_permission(
|
||||||
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
]
|
||||||
|
)
|
||||||
def get(self, request, slug, project_id, pk=None):
|
def get(self, request, slug, project_id, pk=None):
|
||||||
if pk is None:
|
if pk is None:
|
||||||
queryset = (
|
queryset = (
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ class CycleViewSet(BaseViewSet):
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def list(self, request, slug, project_id):
|
def list(self, request, slug, project_id):
|
||||||
queryset = self.get_queryset().filter(archived_at__isnull=True)
|
queryset = self.get_queryset().filter(archived_at__isnull=True)
|
||||||
cycle_view = request.GET.get("cycle_view", "all")
|
cycle_view = request.GET.get("cycle_view", "all")
|
||||||
|
|
@ -370,7 +370,12 @@ class CycleViewSet(BaseViewSet):
|
||||||
return Response(cycle, status=status.HTTP_200_OK)
|
return Response(cycle, status=status.HTTP_200_OK)
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER])
|
@allow_permission(
|
||||||
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
]
|
||||||
|
)
|
||||||
def retrieve(self, request, slug, project_id, pk):
|
def retrieve(self, request, slug, project_id, pk):
|
||||||
queryset = (
|
queryset = (
|
||||||
self.get_queryset().filter(archived_at__isnull=True).filter(pk=pk)
|
self.get_queryset().filter(archived_at__isnull=True).filter(pk=pk)
|
||||||
|
|
@ -497,7 +502,6 @@ class CycleViewSet(BaseViewSet):
|
||||||
|
|
||||||
|
|
||||||
class CycleDateCheckEndpoint(BaseAPIView):
|
class CycleDateCheckEndpoint(BaseAPIView):
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER])
|
||||||
def post(self, request, slug, project_id):
|
def post(self, request, slug, project_id):
|
||||||
start_date = request.data.get("start_date", False)
|
start_date = request.data.get("start_date", False)
|
||||||
|
|
@ -566,7 +570,6 @@ class CycleFavoriteViewSet(BaseViewSet):
|
||||||
|
|
||||||
|
|
||||||
class TransferCycleIssueEndpoint(BaseAPIView):
|
class TransferCycleIssueEndpoint(BaseAPIView):
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER])
|
||||||
def post(self, request, slug, project_id, cycle_id):
|
def post(self, request, slug, project_id, cycle_id):
|
||||||
new_cycle_id = request.data.get("new_cycle_id", False)
|
new_cycle_id = request.data.get("new_cycle_id", False)
|
||||||
|
|
@ -977,8 +980,7 @@ class TransferCycleIssueEndpoint(BaseAPIView):
|
||||||
|
|
||||||
|
|
||||||
class CycleUserPropertiesEndpoint(BaseAPIView):
|
class CycleUserPropertiesEndpoint(BaseAPIView):
|
||||||
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST])
|
|
||||||
def patch(self, request, slug, project_id, cycle_id):
|
def patch(self, request, slug, project_id, cycle_id):
|
||||||
cycle_properties = CycleUserProperties.objects.get(
|
cycle_properties = CycleUserProperties.objects.get(
|
||||||
user=request.user,
|
user=request.user,
|
||||||
|
|
@ -1001,7 +1003,7 @@ class CycleUserPropertiesEndpoint(BaseAPIView):
|
||||||
serializer = CycleUserPropertiesSerializer(cycle_properties)
|
serializer = CycleUserPropertiesSerializer(cycle_properties)
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def get(self, request, slug, project_id, cycle_id):
|
def get(self, request, slug, project_id, cycle_id):
|
||||||
cycle_properties, _ = CycleUserProperties.objects.get_or_create(
|
cycle_properties, _ = CycleUserProperties.objects.get_or_create(
|
||||||
user=request.user,
|
user=request.user,
|
||||||
|
|
@ -1014,10 +1016,8 @@ class CycleUserPropertiesEndpoint(BaseAPIView):
|
||||||
|
|
||||||
|
|
||||||
class CycleProgressEndpoint(BaseAPIView):
|
class CycleProgressEndpoint(BaseAPIView):
|
||||||
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST])
|
|
||||||
def get(self, request, slug, project_id, cycle_id):
|
def get(self, request, slug, project_id, cycle_id):
|
||||||
|
|
||||||
aggregate_estimates = (
|
aggregate_estimates = (
|
||||||
Issue.issue_objects.filter(
|
Issue.issue_objects.filter(
|
||||||
estimate_point__estimate__type="points",
|
estimate_point__estimate__type="points",
|
||||||
|
|
@ -1148,10 +1148,9 @@ class CycleProgressEndpoint(BaseAPIView):
|
||||||
status=status.HTTP_200_OK,
|
status=status.HTTP_200_OK,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class CycleAnalyticsEndpoint(BaseAPIView):
|
class CycleAnalyticsEndpoint(BaseAPIView):
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def get(self, request, slug, project_id, cycle_id):
|
def get(self, request, slug, project_id, cycle_id):
|
||||||
analytic_type = request.GET.get("type", "issues")
|
analytic_type = request.GET.get("type", "issues")
|
||||||
cycle = (
|
cycle = (
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,12 @@ class CycleIssueViewSet(BaseViewSet):
|
||||||
)
|
)
|
||||||
|
|
||||||
@method_decorator(gzip_page)
|
@method_decorator(gzip_page)
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER])
|
@allow_permission(
|
||||||
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
]
|
||||||
|
)
|
||||||
def list(self, request, slug, project_id, cycle_id):
|
def list(self, request, slug, project_id, cycle_id):
|
||||||
order_by_param = request.GET.get("order_by", "created_at")
|
order_by_param = request.GET.get("order_by", "created_at")
|
||||||
filters = issue_filters(request.query_params, "GET")
|
filters = issue_filters(request.query_params, "GET")
|
||||||
|
|
|
||||||
|
|
@ -52,23 +52,28 @@ from .. import BaseAPIView
|
||||||
|
|
||||||
|
|
||||||
def dashboard_overview_stats(self, request, slug):
|
def dashboard_overview_stats(self, request, slug):
|
||||||
extra_filters = {}
|
|
||||||
if WorkspaceMember.objects.filter(
|
|
||||||
workspace__slug=slug,
|
|
||||||
member=request.user,
|
|
||||||
role=5,
|
|
||||||
is_active=True,
|
|
||||||
).exists():
|
|
||||||
extra_filters = {"created_by": request.user}
|
|
||||||
|
|
||||||
assigned_issues = (
|
assigned_issues = (
|
||||||
Issue.issue_objects.filter(
|
Issue.issue_objects.filter(
|
||||||
project__project_projectmember__is_active=True,
|
project__project_projectmember__is_active=True,
|
||||||
project__project_projectmember__member=request.user,
|
project__project_projectmember__member=request.user,
|
||||||
workspace__slug=slug,
|
workspace__slug=slug,
|
||||||
assignees__in=[request.user],
|
assignees__in=[request.user],
|
||||||
|
).filter(
|
||||||
|
Q(
|
||||||
|
project__project_projectmember__role=5,
|
||||||
|
project__guest_view_all_features=True,
|
||||||
|
)
|
||||||
|
| Q(
|
||||||
|
project__project_projectmember__role=5,
|
||||||
|
project__guest_view_all_features=False,
|
||||||
|
created_by=self.request.user,
|
||||||
|
)
|
||||||
|
|
|
||||||
|
# For other roles (role < 5), show all issues
|
||||||
|
Q(project__project_projectmember__role__gt=5),
|
||||||
|
project__project_projectmember__member=self.request.user,
|
||||||
|
project__project_projectmember__is_active=True,
|
||||||
)
|
)
|
||||||
.filter(**extra_filters)
|
|
||||||
.count()
|
.count()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -80,8 +85,22 @@ def dashboard_overview_stats(self, request, slug):
|
||||||
project__project_projectmember__member=request.user,
|
project__project_projectmember__member=request.user,
|
||||||
workspace__slug=slug,
|
workspace__slug=slug,
|
||||||
assignees__in=[request.user],
|
assignees__in=[request.user],
|
||||||
|
).filter(
|
||||||
|
Q(
|
||||||
|
project__project_projectmember__role=5,
|
||||||
|
project__guest_view_all_features=True,
|
||||||
|
)
|
||||||
|
| Q(
|
||||||
|
project__project_projectmember__role=5,
|
||||||
|
project__guest_view_all_features=False,
|
||||||
|
created_by=self.request.user,
|
||||||
|
)
|
||||||
|
|
|
||||||
|
# For other roles (role < 5), show all issues
|
||||||
|
Q(project__project_projectmember__role__gt=5),
|
||||||
|
project__project_projectmember__member=self.request.user,
|
||||||
|
project__project_projectmember__is_active=True,
|
||||||
)
|
)
|
||||||
.filter(**extra_filters)
|
|
||||||
.count()
|
.count()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -91,8 +110,22 @@ def dashboard_overview_stats(self, request, slug):
|
||||||
project__project_projectmember__is_active=True,
|
project__project_projectmember__is_active=True,
|
||||||
project__project_projectmember__member=request.user,
|
project__project_projectmember__member=request.user,
|
||||||
created_by_id=request.user.id,
|
created_by_id=request.user.id,
|
||||||
|
).filter(
|
||||||
|
Q(
|
||||||
|
project__project_projectmember__role=5,
|
||||||
|
project__guest_view_all_features=True,
|
||||||
|
)
|
||||||
|
| Q(
|
||||||
|
project__project_projectmember__role=5,
|
||||||
|
project__guest_view_all_features=False,
|
||||||
|
created_by=self.request.user,
|
||||||
|
)
|
||||||
|
|
|
||||||
|
# For other roles (role < 5), show all issues
|
||||||
|
Q(project__project_projectmember__role__gt=5),
|
||||||
|
project__project_projectmember__member=self.request.user,
|
||||||
|
project__project_projectmember__is_active=True,
|
||||||
)
|
)
|
||||||
.filter(**extra_filters)
|
|
||||||
.count()
|
.count()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -103,8 +136,22 @@ def dashboard_overview_stats(self, request, slug):
|
||||||
project__project_projectmember__member=request.user,
|
project__project_projectmember__member=request.user,
|
||||||
assignees__in=[request.user],
|
assignees__in=[request.user],
|
||||||
state__group="completed",
|
state__group="completed",
|
||||||
|
).filter(
|
||||||
|
Q(
|
||||||
|
project__project_projectmember__role=5,
|
||||||
|
project__guest_view_all_features=True,
|
||||||
|
)
|
||||||
|
| Q(
|
||||||
|
project__project_projectmember__role=5,
|
||||||
|
project__guest_view_all_features=False,
|
||||||
|
created_by=self.request.user,
|
||||||
|
)
|
||||||
|
|
|
||||||
|
# For other roles (role < 5), show all issues
|
||||||
|
Q(project__project_projectmember__role__gt=5),
|
||||||
|
project__project_projectmember__member=self.request.user,
|
||||||
|
project__project_projectmember__is_active=True,
|
||||||
)
|
)
|
||||||
.filter(**extra_filters)
|
|
||||||
.count()
|
.count()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,12 @@ def generate_random_name(length=10):
|
||||||
|
|
||||||
class ProjectEstimatePointEndpoint(BaseAPIView):
|
class ProjectEstimatePointEndpoint(BaseAPIView):
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER])
|
@allow_permission(
|
||||||
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
]
|
||||||
|
)
|
||||||
def get(self, request, slug, project_id):
|
def get(self, request, slug, project_id):
|
||||||
project = Project.objects.get(workspace__slug=slug, pk=project_id)
|
project = Project.objects.get(workspace__slug=slug, pk=project_id)
|
||||||
if project.estimate_id is not None:
|
if project.estimate_id is not None:
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ class InboxIssueViewSet(BaseViewSet):
|
||||||
)
|
)
|
||||||
).distinct()
|
).distinct()
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def list(self, request, slug, project_id):
|
def list(self, request, slug, project_id):
|
||||||
inbox_id = Inbox.objects.get(
|
inbox_id = Inbox.objects.get(
|
||||||
workspace__slug=slug, project_id=project_id
|
workspace__slug=slug, project_id=project_id
|
||||||
|
|
@ -338,7 +338,7 @@ class InboxIssueViewSet(BaseViewSet):
|
||||||
is_active=True,
|
is_active=True,
|
||||||
)
|
)
|
||||||
# Only project members admins and created_by users can access this endpoint
|
# Only project members admins and created_by users can access this endpoint
|
||||||
if project_member.role <= 10 and str(inbox_issue.created_by_id) != str(
|
if project_member.role <= 5 and str(inbox_issue.created_by_id) != str(
|
||||||
request.user.id
|
request.user.id
|
||||||
):
|
):
|
||||||
return Response(
|
return Response(
|
||||||
|
|
@ -371,9 +371,8 @@ class InboxIssueViewSet(BaseViewSet):
|
||||||
workspace__slug=slug,
|
workspace__slug=slug,
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
)
|
)
|
||||||
# Only allow guests and viewers to edit name and description
|
# Only allow guests to edit name and description
|
||||||
if project_member.role <= 10:
|
if project_member.role <= 5:
|
||||||
# viewers and guests since only viewers and guests
|
|
||||||
issue_data = {
|
issue_data = {
|
||||||
"name": issue_data.get("name", issue.name),
|
"name": issue_data.get("name", issue.name),
|
||||||
"description_html": issue_data.get(
|
"description_html": issue_data.get(
|
||||||
|
|
@ -415,7 +414,7 @@ class InboxIssueViewSet(BaseViewSet):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Only project admins and members can edit inbox issue attributes
|
# Only project admins and members can edit inbox issue attributes
|
||||||
if project_member.role > 10:
|
if project_member.role > 5:
|
||||||
serializer = InboxIssueSerializer(
|
serializer = InboxIssueSerializer(
|
||||||
inbox_issue, data=request.data, partial=True
|
inbox_issue, data=request.data, partial=True
|
||||||
)
|
)
|
||||||
|
|
@ -515,7 +514,10 @@ class InboxIssueViewSet(BaseViewSet):
|
||||||
return Response(serializer, status=status.HTTP_200_OK)
|
return Response(serializer, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
@allow_permission(
|
@allow_permission(
|
||||||
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER],
|
allowed_roles=[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
],
|
||||||
creator=True,
|
creator=True,
|
||||||
model=Issue,
|
model=Issue,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,11 @@ from plane.app.serializers import (
|
||||||
IssueActivitySerializer,
|
IssueActivitySerializer,
|
||||||
IssueCommentSerializer,
|
IssueCommentSerializer,
|
||||||
)
|
)
|
||||||
from plane.app.permissions import ProjectEntityPermission, allow_permission, ROLE
|
from plane.app.permissions import (
|
||||||
|
ProjectEntityPermission,
|
||||||
|
allow_permission,
|
||||||
|
ROLE,
|
||||||
|
)
|
||||||
from plane.db.models import (
|
from plane.db.models import (
|
||||||
IssueActivity,
|
IssueActivity,
|
||||||
IssueComment,
|
IssueComment,
|
||||||
|
|
@ -33,7 +37,13 @@ class IssueActivityEndpoint(BaseAPIView):
|
||||||
]
|
]
|
||||||
|
|
||||||
@method_decorator(gzip_page)
|
@method_decorator(gzip_page)
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST, ROLE.VIEWER])
|
@allow_permission(
|
||||||
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
ROLE.GUEST,
|
||||||
|
]
|
||||||
|
)
|
||||||
def get(self, request, slug, project_id, issue_id):
|
def get(self, request, slug, project_id, issue_id):
|
||||||
filters = {}
|
filters = {}
|
||||||
if request.GET.get("created_at__gt", None) is not None:
|
if request.GET.get("created_at__gt", None) is not None:
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,12 @@ class IssueArchiveViewSet(BaseViewSet):
|
||||||
)
|
)
|
||||||
|
|
||||||
@method_decorator(gzip_page)
|
@method_decorator(gzip_page)
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER])
|
@allow_permission(
|
||||||
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
]
|
||||||
|
)
|
||||||
def list(self, request, slug, project_id):
|
def list(self, request, slug, project_id):
|
||||||
filters = issue_filters(request.query_params, "GET")
|
filters = issue_filters(request.query_params, "GET")
|
||||||
show_sub_issues = request.GET.get("show_sub_issues", "true")
|
show_sub_issues = request.GET.get("show_sub_issues", "true")
|
||||||
|
|
@ -213,7 +218,12 @@ class IssueArchiveViewSet(BaseViewSet):
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER])
|
@allow_permission(
|
||||||
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
]
|
||||||
|
)
|
||||||
def retrieve(self, request, slug, project_id, pk=None):
|
def retrieve(self, request, slug, project_id, pk=None):
|
||||||
issue = (
|
issue = (
|
||||||
self.get_queryset()
|
self.get_queryset()
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,13 @@ class IssueAttachmentEndpoint(BaseAPIView):
|
||||||
|
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST, ROLE.VIEWER])
|
@allow_permission(
|
||||||
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
ROLE.GUEST,
|
||||||
|
]
|
||||||
|
)
|
||||||
def get(self, request, slug, project_id, issue_id):
|
def get(self, request, slug, project_id, issue_id):
|
||||||
issue_attachments = IssueAttachment.objects.filter(
|
issue_attachments = IssueAttachment.objects.filter(
|
||||||
issue_id=issue_id, workspace__slug=slug, project_id=project_id
|
issue_id=issue_id, workspace__slug=slug, project_id=project_id
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ from plane.bgtasks.webhook_task import model_activity
|
||||||
|
|
||||||
|
|
||||||
class IssueListEndpoint(BaseAPIView):
|
class IssueListEndpoint(BaseAPIView):
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def get(self, request, slug, project_id):
|
def get(self, request, slug, project_id):
|
||||||
issue_ids = request.GET.get("issues", False)
|
issue_ids = request.GET.get("issues", False)
|
||||||
|
|
||||||
|
|
@ -232,8 +232,9 @@ class IssueViewSet(BaseViewSet):
|
||||||
).distinct()
|
).distinct()
|
||||||
|
|
||||||
@method_decorator(gzip_page)
|
@method_decorator(gzip_page)
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def list(self, request, slug, project_id):
|
def list(self, request, slug, project_id):
|
||||||
|
project = Project.objects.get(pk=project_id, workspace__slug=slug)
|
||||||
filters = issue_filters(request.query_params, "GET")
|
filters = issue_filters(request.query_params, "GET")
|
||||||
order_by_param = request.GET.get("order_by", "-created_at")
|
order_by_param = request.GET.get("order_by", "-created_at")
|
||||||
|
|
||||||
|
|
@ -264,13 +265,16 @@ class IssueViewSet(BaseViewSet):
|
||||||
entity_identifier=project_id,
|
entity_identifier=project_id,
|
||||||
user_id=request.user.id,
|
user_id=request.user.id,
|
||||||
)
|
)
|
||||||
if ProjectMember.objects.filter(
|
if (
|
||||||
workspace__slug=slug,
|
ProjectMember.objects.filter(
|
||||||
project_id=project_id,
|
workspace__slug=slug,
|
||||||
member=request.user,
|
project_id=project_id,
|
||||||
role=5,
|
member=request.user,
|
||||||
is_active=True,
|
role=5,
|
||||||
).exists():
|
is_active=True,
|
||||||
|
).exists()
|
||||||
|
and not project.guest_view_all_features
|
||||||
|
):
|
||||||
issue_queryset = issue_queryset.filter(created_by=request.user)
|
issue_queryset = issue_queryset.filter(created_by=request.user)
|
||||||
|
|
||||||
if group_by:
|
if group_by:
|
||||||
|
|
@ -440,9 +444,17 @@ class IssueViewSet(BaseViewSet):
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
@allow_permission(
|
@allow_permission(
|
||||||
[ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER], creator=True, model=Issue
|
allowed_roles=[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
ROLE.GUEST,
|
||||||
|
],
|
||||||
|
creator=True,
|
||||||
|
model=Issue,
|
||||||
)
|
)
|
||||||
def retrieve(self, request, slug, project_id, pk=None):
|
def retrieve(self, request, slug, project_id, pk=None):
|
||||||
|
project = Project.objects.get(pk=project_id, workspace__slug=slug)
|
||||||
|
|
||||||
issue = (
|
issue = (
|
||||||
self.get_queryset()
|
self.get_queryset()
|
||||||
.filter(pk=pk)
|
.filter(pk=pk)
|
||||||
|
|
@ -511,6 +523,27 @@ class IssueViewSet(BaseViewSet):
|
||||||
status=status.HTTP_404_NOT_FOUND,
|
status=status.HTTP_404_NOT_FOUND,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
"""
|
||||||
|
if the role is guest and guest_view_all_features is false and owned by is not
|
||||||
|
the requesting user then dont show the issue
|
||||||
|
"""
|
||||||
|
|
||||||
|
if (
|
||||||
|
ProjectMember.objects.filter(
|
||||||
|
workspace__slug=slug,
|
||||||
|
project_id=project_id,
|
||||||
|
member=request.user,
|
||||||
|
role=5,
|
||||||
|
is_active=True,
|
||||||
|
).exists()
|
||||||
|
and not project.guest_view_all_features
|
||||||
|
and not issue.created_by == request.user
|
||||||
|
):
|
||||||
|
return Response(
|
||||||
|
{"error": "You are not allowed to view this issue"},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
|
)
|
||||||
|
|
||||||
recent_visited_task.delay(
|
recent_visited_task.delay(
|
||||||
slug=slug,
|
slug=slug,
|
||||||
entity_name="issue",
|
entity_name="issue",
|
||||||
|
|
@ -522,7 +555,9 @@ class IssueViewSet(BaseViewSet):
|
||||||
serializer = IssueDetailSerializer(issue, expand=self.expand)
|
serializer = IssueDetailSerializer(issue, expand=self.expand)
|
||||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
@allow_permission(
|
||||||
|
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER], creator=True, model=Issue
|
||||||
|
)
|
||||||
def partial_update(self, request, slug, project_id, pk=None):
|
def partial_update(self, request, slug, project_id, pk=None):
|
||||||
issue = (
|
issue = (
|
||||||
self.get_queryset()
|
self.get_queryset()
|
||||||
|
|
@ -618,7 +653,7 @@ class IssueViewSet(BaseViewSet):
|
||||||
|
|
||||||
|
|
||||||
class IssueUserDisplayPropertyEndpoint(BaseAPIView):
|
class IssueUserDisplayPropertyEndpoint(BaseAPIView):
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST, ROLE.VIEWER])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def patch(self, request, slug, project_id):
|
def patch(self, request, slug, project_id):
|
||||||
issue_property = IssueUserProperty.objects.get(
|
issue_property = IssueUserProperty.objects.get(
|
||||||
user=request.user,
|
user=request.user,
|
||||||
|
|
@ -638,7 +673,13 @@ class IssueUserDisplayPropertyEndpoint(BaseAPIView):
|
||||||
serializer = IssueUserPropertySerializer(issue_property)
|
serializer = IssueUserPropertySerializer(issue_property)
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST, ROLE.VIEWER])
|
@allow_permission(
|
||||||
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
ROLE.GUEST,
|
||||||
|
]
|
||||||
|
)
|
||||||
def get(self, request, slug, project_id):
|
def get(self, request, slug, project_id):
|
||||||
issue_property, _ = IssueUserProperty.objects.get_or_create(
|
issue_property, _ = IssueUserProperty.objects.get_or_create(
|
||||||
user=request.user, project_id=project_id
|
user=request.user, project_id=project_id
|
||||||
|
|
@ -719,7 +760,7 @@ class IssuePaginatedViewSet(BaseViewSet):
|
||||||
|
|
||||||
return paginated_data
|
return paginated_data
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def list(self, request, slug, project_id):
|
def list(self, request, slug, project_id):
|
||||||
cursor = request.GET.get("cursor", None)
|
cursor = request.GET.get("cursor", None)
|
||||||
is_description_required = request.GET.get("description", False)
|
is_description_required = request.GET.get("description", False)
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ from plane.app.serializers import (
|
||||||
IssueCommentSerializer,
|
IssueCommentSerializer,
|
||||||
CommentReactionSerializer,
|
CommentReactionSerializer,
|
||||||
)
|
)
|
||||||
from plane.app.permissions import ProjectLitePermission, allow_permission, ROLE
|
from plane.app.permissions import allow_permission, ROLE
|
||||||
from plane.db.models import (
|
from plane.db.models import (
|
||||||
IssueComment,
|
IssueComment,
|
||||||
ProjectMember,
|
ProjectMember,
|
||||||
|
|
@ -63,7 +63,12 @@ class IssueCommentViewSet(BaseViewSet):
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST, ROLE.VIEWER])
|
@allow_permission(
|
||||||
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
]
|
||||||
|
)
|
||||||
def create(self, request, slug, project_id, issue_id):
|
def create(self, request, slug, project_id, issue_id):
|
||||||
serializer = IssueCommentSerializer(data=request.data)
|
serializer = IssueCommentSerializer(data=request.data)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
|
|
@ -156,9 +161,6 @@ class IssueCommentViewSet(BaseViewSet):
|
||||||
class CommentReactionViewSet(BaseViewSet):
|
class CommentReactionViewSet(BaseViewSet):
|
||||||
serializer_class = CommentReactionSerializer
|
serializer_class = CommentReactionSerializer
|
||||||
model = CommentReaction
|
model = CommentReaction
|
||||||
permission_classes = [
|
|
||||||
ProjectLitePermission,
|
|
||||||
]
|
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return (
|
return (
|
||||||
|
|
@ -176,6 +178,12 @@ class CommentReactionViewSet(BaseViewSet):
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@allow_permission(
|
||||||
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
]
|
||||||
|
)
|
||||||
def create(self, request, slug, project_id, comment_id):
|
def create(self, request, slug, project_id, comment_id):
|
||||||
serializer = CommentReactionSerializer(data=request.data)
|
serializer = CommentReactionSerializer(data=request.data)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
|
|
@ -198,6 +206,12 @@ class CommentReactionViewSet(BaseViewSet):
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
@allow_permission(
|
||||||
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
]
|
||||||
|
)
|
||||||
def destroy(self, request, slug, project_id, comment_id, reaction_code):
|
def destroy(self, request, slug, project_id, comment_id, reaction_code):
|
||||||
comment_reaction = CommentReaction.objects.get(
|
comment_reaction = CommentReaction.objects.get(
|
||||||
workspace__slug=slug,
|
workspace__slug=slug,
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ class LabelViewSet(BaseViewSet):
|
||||||
@invalidate_cache(
|
@invalidate_cache(
|
||||||
path="/api/workspaces/:slug/labels/", url_params=True, user=False
|
path="/api/workspaces/:slug/labels/", url_params=True, user=False
|
||||||
)
|
)
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER])
|
@allow_permission([ROLE.ADMIN])
|
||||||
def create(self, request, slug, project_id):
|
def create(self, request, slug, project_id):
|
||||||
try:
|
try:
|
||||||
serializer = LabelSerializer(data=request.data)
|
serializer = LabelSerializer(data=request.data)
|
||||||
|
|
@ -66,14 +66,14 @@ class LabelViewSet(BaseViewSet):
|
||||||
@invalidate_cache(
|
@invalidate_cache(
|
||||||
path="/api/workspaces/:slug/labels/", url_params=True, user=False
|
path="/api/workspaces/:slug/labels/", url_params=True, user=False
|
||||||
)
|
)
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER])
|
@allow_permission([ROLE.ADMIN])
|
||||||
def partial_update(self, request, *args, **kwargs):
|
def partial_update(self, request, *args, **kwargs):
|
||||||
return super().partial_update(request, *args, **kwargs)
|
return super().partial_update(request, *args, **kwargs)
|
||||||
|
|
||||||
@invalidate_cache(
|
@invalidate_cache(
|
||||||
path="/api/workspaces/:slug/labels/", url_params=True, user=False
|
path="/api/workspaces/:slug/labels/", url_params=True, user=False
|
||||||
)
|
)
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER])
|
@allow_permission([ROLE.ADMIN])
|
||||||
def destroy(self, request, *args, **kwargs):
|
def destroy(self, request, *args, **kwargs):
|
||||||
return super().destroy(request, *args, **kwargs)
|
return super().destroy(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -317,7 +317,12 @@ class ModuleViewSet(BaseViewSet):
|
||||||
.order_by("-is_favorite", "-created_at")
|
.order_by("-is_favorite", "-created_at")
|
||||||
)
|
)
|
||||||
|
|
||||||
allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER])
|
allow_permission(
|
||||||
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
def create(self, request, slug, project_id):
|
def create(self, request, slug, project_id):
|
||||||
project = Project.objects.get(workspace__slug=slug, pk=project_id)
|
project = Project.objects.get(workspace__slug=slug, pk=project_id)
|
||||||
|
|
@ -381,7 +386,7 @@ class ModuleViewSet(BaseViewSet):
|
||||||
return Response(module, status=status.HTTP_201_CREATED)
|
return Response(module, status=status.HTTP_201_CREATED)
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST])
|
allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
|
|
||||||
def list(self, request, slug, project_id):
|
def list(self, request, slug, project_id):
|
||||||
queryset = self.get_queryset().filter(archived_at__isnull=True)
|
queryset = self.get_queryset().filter(archived_at__isnull=True)
|
||||||
|
|
@ -430,7 +435,12 @@ class ModuleViewSet(BaseViewSet):
|
||||||
)
|
)
|
||||||
return Response(modules, status=status.HTTP_200_OK)
|
return Response(modules, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER])
|
allow_permission(
|
||||||
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
def retrieve(self, request, slug, project_id, pk):
|
def retrieve(self, request, slug, project_id, pk):
|
||||||
queryset = (
|
queryset = (
|
||||||
|
|
@ -861,7 +871,7 @@ class ModuleFavoriteViewSet(BaseViewSet):
|
||||||
|
|
||||||
class ModuleUserPropertiesEndpoint(BaseAPIView):
|
class ModuleUserPropertiesEndpoint(BaseAPIView):
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def patch(self, request, slug, project_id, module_id):
|
def patch(self, request, slug, project_id, module_id):
|
||||||
module_properties = ModuleUserProperties.objects.get(
|
module_properties = ModuleUserProperties.objects.get(
|
||||||
user=request.user,
|
user=request.user,
|
||||||
|
|
@ -884,7 +894,7 @@ class ModuleUserPropertiesEndpoint(BaseAPIView):
|
||||||
serializer = ModuleUserPropertiesSerializer(module_properties)
|
serializer = ModuleUserPropertiesSerializer(module_properties)
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def get(self, request, slug, project_id, module_id):
|
def get(self, request, slug, project_id, module_id):
|
||||||
module_properties, _ = ModuleUserProperties.objects.get_or_create(
|
module_properties, _ = ModuleUserProperties.objects.get_or_create(
|
||||||
user=request.user,
|
user=request.user,
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,12 @@ class ModuleIssueViewSet(BaseViewSet):
|
||||||
).distinct()
|
).distinct()
|
||||||
|
|
||||||
@method_decorator(gzip_page)
|
@method_decorator(gzip_page)
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER])
|
@allow_permission(
|
||||||
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
]
|
||||||
|
)
|
||||||
def list(self, request, slug, project_id, module_id):
|
def list(self, request, slug, project_id, module_id):
|
||||||
filters = issue_filters(request.query_params, "GET")
|
filters = issue_filters(request.query_params, "GET")
|
||||||
issue_queryset = self.get_queryset().filter(**filters)
|
issue_queryset = self.get_queryset().filter(**filters)
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ class NotificationViewSet(BaseViewSet, BasePaginator):
|
||||||
)
|
)
|
||||||
|
|
||||||
@allow_permission(
|
@allow_permission(
|
||||||
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST],
|
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER],
|
||||||
level="WORKSPACE",
|
level="WORKSPACE",
|
||||||
)
|
)
|
||||||
def list(self, request, slug):
|
def list(self, request, slug):
|
||||||
|
|
@ -174,7 +174,7 @@ class NotificationViewSet(BaseViewSet, BasePaginator):
|
||||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
@allow_permission(
|
@allow_permission(
|
||||||
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST],
|
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST],
|
||||||
level="WORKSPACE",
|
level="WORKSPACE",
|
||||||
)
|
)
|
||||||
def partial_update(self, request, slug, pk):
|
def partial_update(self, request, slug, pk):
|
||||||
|
|
@ -195,8 +195,7 @@ class NotificationViewSet(BaseViewSet, BasePaginator):
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
@allow_permission(
|
@allow_permission(
|
||||||
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST],
|
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE"
|
||||||
level="WORKSPACE",
|
|
||||||
)
|
)
|
||||||
def mark_read(self, request, slug, pk):
|
def mark_read(self, request, slug, pk):
|
||||||
notification = Notification.objects.get(
|
notification = Notification.objects.get(
|
||||||
|
|
@ -246,7 +245,7 @@ class NotificationViewSet(BaseViewSet, BasePaginator):
|
||||||
|
|
||||||
class UnreadNotificationEndpoint(BaseAPIView):
|
class UnreadNotificationEndpoint(BaseAPIView):
|
||||||
@allow_permission(
|
@allow_permission(
|
||||||
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST],
|
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST],
|
||||||
level="WORKSPACE",
|
level="WORKSPACE",
|
||||||
)
|
)
|
||||||
def get(self, request, slug):
|
def get(self, request, slug):
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,10 @@ from plane.db.models import (
|
||||||
UserFavorite,
|
UserFavorite,
|
||||||
ProjectMember,
|
ProjectMember,
|
||||||
ProjectPage,
|
ProjectPage,
|
||||||
|
Project,
|
||||||
)
|
)
|
||||||
from plane.utils.error_codes import ERROR_CODES
|
from plane.utils.error_codes import ERROR_CODES
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
from ..base import BaseAPIView, BaseViewSet
|
from ..base import BaseAPIView, BaseViewSet
|
||||||
|
|
||||||
|
|
@ -120,7 +122,7 @@ class PageViewSet(BaseViewSet):
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def create(self, request, slug, project_id):
|
def create(self, request, slug, project_id):
|
||||||
serializer = PageSerializer(
|
serializer = PageSerializer(
|
||||||
data=request.data,
|
data=request.data,
|
||||||
|
|
@ -142,7 +144,7 @@ class PageViewSet(BaseViewSet):
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def partial_update(self, request, slug, project_id, pk):
|
def partial_update(self, request, slug, project_id, pk):
|
||||||
try:
|
try:
|
||||||
page = Page.objects.get(
|
page = Page.objects.get(
|
||||||
|
|
@ -208,9 +210,38 @@ class PageViewSet(BaseViewSet):
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER])
|
@allow_permission(
|
||||||
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
ROLE.GUEST,
|
||||||
|
]
|
||||||
|
)
|
||||||
def retrieve(self, request, slug, project_id, pk=None):
|
def retrieve(self, request, slug, project_id, pk=None):
|
||||||
page = self.get_queryset().filter(pk=pk).first()
|
page = self.get_queryset().filter(pk=pk).first()
|
||||||
|
project = Project.objects.get(pk=project_id)
|
||||||
|
|
||||||
|
"""
|
||||||
|
if the role is guest and guest_view_all_features is false and owned by is not
|
||||||
|
the requesting user then dont show the page
|
||||||
|
"""
|
||||||
|
|
||||||
|
if (
|
||||||
|
ProjectMember.objects.filter(
|
||||||
|
workspace__slug=slug,
|
||||||
|
project_id=project_id,
|
||||||
|
member=request.user,
|
||||||
|
role=5,
|
||||||
|
is_active=True,
|
||||||
|
).exists()
|
||||||
|
and not project.guest_view_all_features
|
||||||
|
and not page.owned_by == request.user
|
||||||
|
):
|
||||||
|
return Response(
|
||||||
|
{"error": "You are not allowed to view this page"},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
|
)
|
||||||
|
|
||||||
if page is None:
|
if page is None:
|
||||||
return Response(
|
return Response(
|
||||||
{"error": "Page not found"},
|
{"error": "Page not found"},
|
||||||
|
|
@ -234,7 +265,7 @@ class PageViewSet(BaseViewSet):
|
||||||
status=status.HTTP_200_OK,
|
status=status.HTTP_200_OK,
|
||||||
)
|
)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def lock(self, request, slug, project_id, pk):
|
def lock(self, request, slug, project_id, pk):
|
||||||
page = Page.objects.filter(
|
page = Page.objects.filter(
|
||||||
pk=pk, workspace__slug=slug, projects__id=project_id
|
pk=pk, workspace__slug=slug, projects__id=project_id
|
||||||
|
|
@ -244,7 +275,7 @@ class PageViewSet(BaseViewSet):
|
||||||
page.save()
|
page.save()
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def unlock(self, request, slug, project_id, pk):
|
def unlock(self, request, slug, project_id, pk):
|
||||||
page = Page.objects.filter(
|
page = Page.objects.filter(
|
||||||
pk=pk, workspace__slug=slug, projects__id=project_id
|
pk=pk, workspace__slug=slug, projects__id=project_id
|
||||||
|
|
@ -255,7 +286,7 @@ class PageViewSet(BaseViewSet):
|
||||||
|
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def access(self, request, slug, project_id, pk):
|
def access(self, request, slug, project_id, pk):
|
||||||
access = request.data.get("access", 0)
|
access = request.data.get("access", 0)
|
||||||
page = Page.objects.filter(
|
page = Page.objects.filter(
|
||||||
|
|
@ -278,13 +309,31 @@ class PageViewSet(BaseViewSet):
|
||||||
page.save()
|
page.save()
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER])
|
@allow_permission(
|
||||||
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
ROLE.GUEST,
|
||||||
|
]
|
||||||
|
)
|
||||||
def list(self, request, slug, project_id):
|
def list(self, request, slug, project_id):
|
||||||
queryset = self.get_queryset()
|
queryset = self.get_queryset()
|
||||||
|
project = Project.objects.get(pk=project_id)
|
||||||
|
if (
|
||||||
|
ProjectMember.objects.filter(
|
||||||
|
workspace__slug=slug,
|
||||||
|
project_id=project_id,
|
||||||
|
member=request.user,
|
||||||
|
role=5,
|
||||||
|
is_active=True,
|
||||||
|
).exists()
|
||||||
|
and not project.guest_view_all_features
|
||||||
|
):
|
||||||
|
queryset = queryset.filter(owned_by=request.user)
|
||||||
pages = PageSerializer(queryset, many=True).data
|
pages = PageSerializer(queryset, many=True).data
|
||||||
return Response(pages, status=status.HTTP_200_OK)
|
return Response(pages, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def archive(self, request, slug, project_id, pk):
|
def archive(self, request, slug, project_id, pk):
|
||||||
page = Page.objects.get(
|
page = Page.objects.get(
|
||||||
pk=pk, workspace__slug=slug, projects__id=project_id
|
pk=pk, workspace__slug=slug, projects__id=project_id
|
||||||
|
|
@ -319,7 +368,7 @@ class PageViewSet(BaseViewSet):
|
||||||
status=status.HTTP_200_OK,
|
status=status.HTTP_200_OK,
|
||||||
)
|
)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def unarchive(self, request, slug, project_id, pk):
|
def unarchive(self, request, slug, project_id, pk):
|
||||||
page = Page.objects.get(
|
page = Page.objects.get(
|
||||||
pk=pk, workspace__slug=slug, projects__id=project_id
|
pk=pk, workspace__slug=slug, projects__id=project_id
|
||||||
|
|
@ -477,7 +526,13 @@ class SubPagesEndpoint(BaseAPIView):
|
||||||
|
|
||||||
class PagesDescriptionViewSet(BaseViewSet):
|
class PagesDescriptionViewSet(BaseViewSet):
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER])
|
@allow_permission(
|
||||||
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
ROLE.GUEST,
|
||||||
|
]
|
||||||
|
)
|
||||||
def retrieve(self, request, slug, project_id, pk):
|
def retrieve(self, request, slug, project_id, pk):
|
||||||
page = (
|
page = (
|
||||||
Page.objects.filter(
|
Page.objects.filter(
|
||||||
|
|
@ -507,7 +562,7 @@ class PagesDescriptionViewSet(BaseViewSet):
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def partial_update(self, request, slug, project_id, pk):
|
def partial_update(self, request, slug, project_id, pk):
|
||||||
page = (
|
page = (
|
||||||
Page.objects.filter(
|
Page.objects.filter(
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ from plane.app.permissions import allow_permission, ROLE
|
||||||
class PageVersionEndpoint(BaseAPIView):
|
class PageVersionEndpoint(BaseAPIView):
|
||||||
|
|
||||||
@allow_permission(
|
@allow_permission(
|
||||||
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST]
|
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]
|
||||||
)
|
)
|
||||||
def get(self, request, slug, project_id, page_id, pk=None):
|
def get(self, request, slug, project_id, page_id, pk=None):
|
||||||
# Check if pk is provided
|
# Check if pk is provided
|
||||||
|
|
|
||||||
|
|
@ -71,13 +71,6 @@ class ProjectViewSet(BaseViewSet):
|
||||||
super()
|
super()
|
||||||
.get_queryset()
|
.get_queryset()
|
||||||
.filter(workspace__slug=self.kwargs.get("slug"))
|
.filter(workspace__slug=self.kwargs.get("slug"))
|
||||||
.filter(
|
|
||||||
Q(
|
|
||||||
project_projectmember__member=self.request.user,
|
|
||||||
project_projectmember__is_active=True,
|
|
||||||
)
|
|
||||||
| Q(network=2)
|
|
||||||
)
|
|
||||||
.select_related(
|
.select_related(
|
||||||
"workspace",
|
"workspace",
|
||||||
"workspace__owner",
|
"workspace__owner",
|
||||||
|
|
@ -155,7 +148,7 @@ class ProjectViewSet(BaseViewSet):
|
||||||
)
|
)
|
||||||
|
|
||||||
@allow_permission(
|
@allow_permission(
|
||||||
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST],
|
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST],
|
||||||
level="WORKSPACE",
|
level="WORKSPACE",
|
||||||
)
|
)
|
||||||
def list(self, request, slug):
|
def list(self, request, slug):
|
||||||
|
|
@ -165,6 +158,31 @@ class ProjectViewSet(BaseViewSet):
|
||||||
if field
|
if field
|
||||||
]
|
]
|
||||||
projects = self.get_queryset().order_by("sort_order", "name")
|
projects = self.get_queryset().order_by("sort_order", "name")
|
||||||
|
if WorkspaceMember.objects.filter(
|
||||||
|
member=request.user,
|
||||||
|
workspace__slug=slug,
|
||||||
|
is_active=True,
|
||||||
|
role=5,
|
||||||
|
).exists():
|
||||||
|
projects = projects.filter(
|
||||||
|
project_projectmember__member=self.request.user,
|
||||||
|
project_projectmember__is_active=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if WorkspaceMember.objects.filter(
|
||||||
|
member=request.user,
|
||||||
|
workspace__slug=slug,
|
||||||
|
is_active=True,
|
||||||
|
role=10,
|
||||||
|
).exists():
|
||||||
|
projects = projects.filter(
|
||||||
|
Q(
|
||||||
|
project_projectmember__member=self.request.user,
|
||||||
|
project_projectmember__is_active=True,
|
||||||
|
)
|
||||||
|
| Q(network=2)
|
||||||
|
)
|
||||||
|
|
||||||
if request.GET.get("per_page", False) and request.GET.get(
|
if request.GET.get("per_page", False) and request.GET.get(
|
||||||
"cursor", False
|
"cursor", False
|
||||||
):
|
):
|
||||||
|
|
@ -177,24 +195,13 @@ class ProjectViewSet(BaseViewSet):
|
||||||
).data,
|
).data,
|
||||||
)
|
)
|
||||||
|
|
||||||
if WorkspaceMember.objects.filter(
|
|
||||||
member=request.user,
|
|
||||||
workspace__slug=slug,
|
|
||||||
is_active=True,
|
|
||||||
role__in=[5, 10],
|
|
||||||
).exists():
|
|
||||||
projects = projects.filter(
|
|
||||||
project_projectmember__member=self.request.user,
|
|
||||||
project_projectmember__is_active=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
projects = ProjectListSerializer(
|
projects = ProjectListSerializer(
|
||||||
projects, many=True, fields=fields if fields else None
|
projects, many=True, fields=fields if fields else None
|
||||||
).data
|
).data
|
||||||
return Response(projects, status=status.HTTP_200_OK)
|
return Response(projects, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
@allow_permission(
|
@allow_permission(
|
||||||
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST],
|
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST],
|
||||||
level="WORKSPACE",
|
level="WORKSPACE",
|
||||||
)
|
)
|
||||||
def retrieve(self, request, slug, pk):
|
def retrieve(self, request, slug, pk):
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ class ProjectInvitationsViewset(BaseViewSet):
|
||||||
[
|
[
|
||||||
email
|
email
|
||||||
for email in emails
|
for email in emails
|
||||||
if int(email.get("role", 10)) > requesting_user.role
|
if int(email.get("role", 5)) > requesting_user.role
|
||||||
]
|
]
|
||||||
):
|
):
|
||||||
return Response(
|
return Response(
|
||||||
|
|
@ -97,7 +97,7 @@ class ProjectInvitationsViewset(BaseViewSet):
|
||||||
settings.SECRET_KEY,
|
settings.SECRET_KEY,
|
||||||
algorithm="HS256",
|
algorithm="HS256",
|
||||||
),
|
),
|
||||||
role=email.get("role", 10),
|
role=email.get("role", 5),
|
||||||
created_by=request.user,
|
created_by=request.user,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -170,7 +170,7 @@ class UserProjectInvitationsViewset(BaseViewSet):
|
||||||
ProjectMember(
|
ProjectMember(
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
member=request.user,
|
member=request.user,
|
||||||
role=15 if workspace_role >= 15 else 10,
|
role=15 if workspace_role >= 15 else 5,
|
||||||
workspace=workspace,
|
workspace=workspace,
|
||||||
created_by=request.user,
|
created_by=request.user,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -95,9 +95,21 @@ class ProjectMemberViewSet(BaseViewSet):
|
||||||
member=member,
|
member=member,
|
||||||
is_active=True,
|
is_active=True,
|
||||||
).role
|
).role
|
||||||
if workspace_member_role in [5, 10] and member_roles.get(
|
if workspace_member_role in [20] and member_roles.get(member) in [
|
||||||
member
|
5,
|
||||||
) in [15, 20]:
|
15,
|
||||||
|
]:
|
||||||
|
return Response(
|
||||||
|
{
|
||||||
|
"error": "You cannot add a user with role lower than the workspace role"
|
||||||
|
},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
|
)
|
||||||
|
|
||||||
|
if workspace_member_role in [5] and member_roles.get(member) in [
|
||||||
|
15,
|
||||||
|
20,
|
||||||
|
]:
|
||||||
return Response(
|
return Response(
|
||||||
{
|
{
|
||||||
"error": "You cannot add a user with role higher than the workspace role"
|
"error": "You cannot add a user with role higher than the workspace role"
|
||||||
|
|
@ -143,7 +155,7 @@ class ProjectMemberViewSet(BaseViewSet):
|
||||||
bulk_project_members.append(
|
bulk_project_members.append(
|
||||||
ProjectMember(
|
ProjectMember(
|
||||||
member_id=member.get("member_id"),
|
member_id=member.get("member_id"),
|
||||||
role=member.get("role", 10),
|
role=member.get("role", 5),
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
workspace_id=project.workspace_id,
|
workspace_id=project.workspace_id,
|
||||||
sort_order=(
|
sort_order=(
|
||||||
|
|
@ -189,7 +201,7 @@ class ProjectMemberViewSet(BaseViewSet):
|
||||||
# Return the serialized data
|
# Return the serialized data
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def list(self, request, slug, project_id):
|
def list(self, request, slug, project_id):
|
||||||
# Get the list of project members for the project
|
# Get the list of project members for the project
|
||||||
project_members = ProjectMember.objects.filter(
|
project_members = ProjectMember.objects.filter(
|
||||||
|
|
@ -230,7 +242,7 @@ class ProjectMemberViewSet(BaseViewSet):
|
||||||
member=project_member.member,
|
member=project_member.member,
|
||||||
is_active=True,
|
is_active=True,
|
||||||
).role
|
).role
|
||||||
if workspace_role in [5, 10] and int(
|
if workspace_role in [5] and int(
|
||||||
request.data.get("role", project_member.role)
|
request.data.get("role", project_member.role)
|
||||||
) in [15, 20]:
|
) in [15, 20]:
|
||||||
return Response(
|
return Response(
|
||||||
|
|
@ -261,7 +273,7 @@ class ProjectMemberViewSet(BaseViewSet):
|
||||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER])
|
@allow_permission([ROLE.ADMIN])
|
||||||
def destroy(self, request, slug, project_id, pk):
|
def destroy(self, request, slug, project_id, pk):
|
||||||
project_member = ProjectMember.objects.get(
|
project_member = ProjectMember.objects.get(
|
||||||
workspace__slug=slug,
|
workspace__slug=slug,
|
||||||
|
|
@ -298,7 +310,7 @@ class ProjectMemberViewSet(BaseViewSet):
|
||||||
project_member.save()
|
project_member.save()
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def leave(self, request, slug, project_id):
|
def leave(self, request, slug, project_id):
|
||||||
project_member = ProjectMember.objects.get(
|
project_member = ProjectMember.objects.get(
|
||||||
workspace__slug=slug,
|
workspace__slug=slug,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@ from rest_framework import status
|
||||||
from .. import BaseViewSet
|
from .. import BaseViewSet
|
||||||
from plane.app.serializers import StateSerializer
|
from plane.app.serializers import StateSerializer
|
||||||
from plane.app.permissions import (
|
from plane.app.permissions import (
|
||||||
ProjectEntityPermission,
|
ROLE,
|
||||||
|
allow_permission
|
||||||
)
|
)
|
||||||
from plane.db.models import State, Issue
|
from plane.db.models import State, Issue
|
||||||
from plane.utils.cache import invalidate_cache
|
from plane.utils.cache import invalidate_cache
|
||||||
|
|
@ -18,9 +19,6 @@ from plane.utils.cache import invalidate_cache
|
||||||
class StateViewSet(BaseViewSet):
|
class StateViewSet(BaseViewSet):
|
||||||
serializer_class = StateSerializer
|
serializer_class = StateSerializer
|
||||||
model = State
|
model = State
|
||||||
permission_classes = [
|
|
||||||
ProjectEntityPermission,
|
|
||||||
]
|
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return self.filter_queryset(
|
return self.filter_queryset(
|
||||||
|
|
@ -42,6 +40,7 @@ class StateViewSet(BaseViewSet):
|
||||||
@invalidate_cache(
|
@invalidate_cache(
|
||||||
path="workspaces/:slug/states/", url_params=True, user=False
|
path="workspaces/:slug/states/", url_params=True, user=False
|
||||||
)
|
)
|
||||||
|
@allow_permission([ROLE.ADMIN])
|
||||||
def create(self, request, slug, project_id):
|
def create(self, request, slug, project_id):
|
||||||
serializer = StateSerializer(data=request.data)
|
serializer = StateSerializer(data=request.data)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
|
|
@ -49,6 +48,7 @@ class StateViewSet(BaseViewSet):
|
||||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def list(self, request, slug, project_id):
|
def list(self, request, slug, project_id):
|
||||||
states = StateSerializer(self.get_queryset(), many=True).data
|
states = StateSerializer(self.get_queryset(), many=True).data
|
||||||
grouped = request.GET.get("grouped", False)
|
grouped = request.GET.get("grouped", False)
|
||||||
|
|
@ -65,6 +65,7 @@ class StateViewSet(BaseViewSet):
|
||||||
@invalidate_cache(
|
@invalidate_cache(
|
||||||
path="workspaces/:slug/states/", url_params=True, user=False
|
path="workspaces/:slug/states/", url_params=True, user=False
|
||||||
)
|
)
|
||||||
|
@allow_permission([ROLE.ADMIN])
|
||||||
def mark_as_default(self, request, slug, project_id, pk):
|
def mark_as_default(self, request, slug, project_id, pk):
|
||||||
# Select all the states which are marked as default
|
# Select all the states which are marked as default
|
||||||
_ = State.objects.filter(
|
_ = State.objects.filter(
|
||||||
|
|
@ -78,6 +79,7 @@ class StateViewSet(BaseViewSet):
|
||||||
@invalidate_cache(
|
@invalidate_cache(
|
||||||
path="workspaces/:slug/states/", url_params=True, user=False
|
path="workspaces/:slug/states/", url_params=True, user=False
|
||||||
)
|
)
|
||||||
|
@allow_permission([ROLE.ADMIN])
|
||||||
def destroy(self, request, slug, project_id, pk):
|
def destroy(self, request, slug, project_id, pk):
|
||||||
state = State.objects.get(
|
state = State.objects.get(
|
||||||
is_triage=False,
|
is_triage=False,
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ from plane.db.models import (
|
||||||
Workspace,
|
Workspace,
|
||||||
WorkspaceMember,
|
WorkspaceMember,
|
||||||
ProjectMember,
|
ProjectMember,
|
||||||
|
Project,
|
||||||
)
|
)
|
||||||
from plane.utils.grouper import (
|
from plane.utils.grouper import (
|
||||||
issue_group_values,
|
issue_group_values,
|
||||||
|
|
@ -75,7 +76,7 @@ class WorkspaceViewViewSet(BaseViewSet):
|
||||||
)
|
)
|
||||||
|
|
||||||
@allow_permission(
|
@allow_permission(
|
||||||
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST],
|
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST],
|
||||||
level="WORKSPACE",
|
level="WORKSPACE",
|
||||||
)
|
)
|
||||||
def list(self, request, slug):
|
def list(self, request, slug):
|
||||||
|
|
@ -259,7 +260,7 @@ class WorkspaceViewIssuesViewSet(BaseViewSet):
|
||||||
|
|
||||||
@method_decorator(gzip_page)
|
@method_decorator(gzip_page)
|
||||||
@allow_permission(
|
@allow_permission(
|
||||||
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST],
|
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST],
|
||||||
level="WORKSPACE",
|
level="WORKSPACE",
|
||||||
)
|
)
|
||||||
def list(self, request, slug):
|
def list(self, request, slug):
|
||||||
|
|
@ -272,15 +273,24 @@ class WorkspaceViewIssuesViewSet(BaseViewSet):
|
||||||
.annotate(cycle_id=F("issue_cycle__cycle_id"))
|
.annotate(cycle_id=F("issue_cycle__cycle_id"))
|
||||||
)
|
)
|
||||||
|
|
||||||
if WorkspaceMember.objects.filter(
|
# check for the project member role, if the role is 5 then check for the guest_view_all_features if it is true then show all the issues else show only the issues created by the user
|
||||||
workspace__slug=slug,
|
|
||||||
member=request.user,
|
issue_queryset = issue_queryset.filter(
|
||||||
role=5,
|
Q(
|
||||||
is_active=True,
|
project__project_projectmember__role=5,
|
||||||
).exists():
|
project__guest_view_all_features=True,
|
||||||
issue_queryset = issue_queryset.filter(
|
|
||||||
created_by=request.user,
|
|
||||||
)
|
)
|
||||||
|
| Q(
|
||||||
|
project__project_projectmember__role=5,
|
||||||
|
project__guest_view_all_features=False,
|
||||||
|
created_by=self.request.user,
|
||||||
|
)
|
||||||
|
|
|
||||||
|
# For other roles (role < 5), show all issues
|
||||||
|
Q(project__project_projectmember__role__gt=5),
|
||||||
|
project__project_projectmember__member=self.request.user,
|
||||||
|
project__project_projectmember__is_active=True,
|
||||||
|
)
|
||||||
|
|
||||||
# Issue queryset
|
# Issue queryset
|
||||||
issue_queryset, order_by_param = order_issue_queryset(
|
issue_queryset, order_by_param = order_issue_queryset(
|
||||||
|
|
@ -421,19 +431,21 @@ class IssueViewViewSet(BaseViewSet):
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
|
|
||||||
allow_permission(
|
allow_permission(allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST]
|
|
||||||
)
|
|
||||||
|
|
||||||
def list(self, request, slug, project_id):
|
def list(self, request, slug, project_id):
|
||||||
queryset = self.get_queryset()
|
queryset = self.get_queryset()
|
||||||
if ProjectMember.objects.filter(
|
project = Project.objects.get(id=project_id)
|
||||||
workspace__slug=slug,
|
if (
|
||||||
project_id=project_id,
|
ProjectMember.objects.filter(
|
||||||
member=request.user,
|
workspace__slug=slug,
|
||||||
role=5,
|
project_id=project_id,
|
||||||
is_active=True,
|
member=request.user,
|
||||||
).exists():
|
role=5,
|
||||||
|
is_active=True,
|
||||||
|
).exists()
|
||||||
|
and not project.guest_view_all_features
|
||||||
|
):
|
||||||
queryset = queryset.filter(owned_by=request.user)
|
queryset = queryset.filter(owned_by=request.user)
|
||||||
fields = [
|
fields = [
|
||||||
field
|
field
|
||||||
|
|
@ -445,14 +457,34 @@ class IssueViewViewSet(BaseViewSet):
|
||||||
).data
|
).data
|
||||||
return Response(views, status=status.HTTP_200_OK)
|
return Response(views, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
allow_permission(
|
allow_permission(allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST]
|
|
||||||
)
|
|
||||||
|
|
||||||
def retrieve(self, request, slug, project_id, pk):
|
def retrieve(self, request, slug, project_id, pk):
|
||||||
issue_view = (
|
issue_view = (
|
||||||
self.get_queryset().filter(pk=pk, project_id=project_id).first()
|
self.get_queryset().filter(pk=pk, project_id=project_id).first()
|
||||||
)
|
)
|
||||||
|
project = Project.objects.get(id=project_id)
|
||||||
|
"""
|
||||||
|
if the role is guest and guest_view_all_features is false and owned by is not
|
||||||
|
the requesting user then dont show the view
|
||||||
|
"""
|
||||||
|
|
||||||
|
if (
|
||||||
|
ProjectMember.objects.filter(
|
||||||
|
workspace__slug=slug,
|
||||||
|
project_id=project_id,
|
||||||
|
member=request.user,
|
||||||
|
role=5,
|
||||||
|
is_active=True,
|
||||||
|
).exists()
|
||||||
|
and not project.guest_view_all_features
|
||||||
|
and not issue_view.owned_by == request.user
|
||||||
|
):
|
||||||
|
return Response(
|
||||||
|
{"error": "You are not allowed to view this issue"},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
|
)
|
||||||
|
|
||||||
serializer = IssueViewSerializer(issue_view)
|
serializer = IssueViewSerializer(issue_view)
|
||||||
recent_visited_task.delay(
|
recent_visited_task.delay(
|
||||||
slug=slug,
|
slug=slug,
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ from plane.db.models import (
|
||||||
WorkspaceMember,
|
WorkspaceMember,
|
||||||
WorkspaceTheme,
|
WorkspaceTheme,
|
||||||
)
|
)
|
||||||
|
from plane.app.permissions import ROLE, allow_permission
|
||||||
from plane.utils.cache import cache_response, invalidate_cache
|
from plane.utils.cache import cache_response, invalidate_cache
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views.decorators.cache import cache_control
|
from django.views.decorators.cache import cache_control
|
||||||
|
|
@ -147,11 +148,25 @@ class WorkSpaceViewSet(BaseViewSet):
|
||||||
)
|
)
|
||||||
|
|
||||||
@cache_response(60 * 60 * 2)
|
@cache_response(60 * 60 * 2)
|
||||||
|
@allow_permission(
|
||||||
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
ROLE.MEMBER,
|
||||||
|
ROLE.GUEST,
|
||||||
|
],
|
||||||
|
level="WORKSPACE",
|
||||||
|
)
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
return super().list(request, *args, **kwargs)
|
return super().list(request, *args, **kwargs)
|
||||||
|
|
||||||
@invalidate_cache(path="/api/workspaces/", user=False)
|
@invalidate_cache(path="/api/workspaces/", user=False)
|
||||||
@invalidate_cache(path="/api/users/me/workspaces/")
|
@invalidate_cache(path="/api/users/me/workspaces/")
|
||||||
|
@allow_permission(
|
||||||
|
[
|
||||||
|
ROLE.ADMIN,
|
||||||
|
],
|
||||||
|
level="WORKSPACE",
|
||||||
|
)
|
||||||
def partial_update(self, request, *args, **kwargs):
|
def partial_update(self, request, *args, **kwargs):
|
||||||
return super().partial_update(request, *args, **kwargs)
|
return super().partial_update(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
@ -162,6 +177,7 @@ class WorkSpaceViewSet(BaseViewSet):
|
||||||
@invalidate_cache(
|
@invalidate_cache(
|
||||||
path="/api/users/me/settings/", multiple=True, user=False
|
path="/api/users/me/settings/", multiple=True, user=False
|
||||||
)
|
)
|
||||||
|
@allow_permission([ROLE.ADMIN], level="WORKSPACE")
|
||||||
def destroy(self, request, *args, **kwargs):
|
def destroy(self, request, *args, **kwargs):
|
||||||
return super().destroy(request, *args, **kwargs)
|
return super().destroy(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ class WorkspaceInvitationsViewset(BaseViewSet):
|
||||||
[
|
[
|
||||||
email
|
email
|
||||||
for email in emails
|
for email in emails
|
||||||
if int(email.get("role", 10)) > requesting_user.role
|
if int(email.get("role", 5)) > requesting_user.role
|
||||||
]
|
]
|
||||||
):
|
):
|
||||||
return Response(
|
return Response(
|
||||||
|
|
@ -119,7 +119,7 @@ class WorkspaceInvitationsViewset(BaseViewSet):
|
||||||
settings.SECRET_KEY,
|
settings.SECRET_KEY,
|
||||||
algorithm="HS256",
|
algorithm="HS256",
|
||||||
),
|
),
|
||||||
role=email.get("role", 10),
|
role=email.get("role", 5),
|
||||||
created_by=request.user,
|
created_by=request.user,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ from rest_framework.response import Response
|
||||||
from plane.app.permissions import (
|
from plane.app.permissions import (
|
||||||
WorkSpaceAdminPermission,
|
WorkSpaceAdminPermission,
|
||||||
WorkspaceEntityPermission,
|
WorkspaceEntityPermission,
|
||||||
WorkspaceUserPermission,
|
allow_permission,
|
||||||
|
ROLE
|
||||||
)
|
)
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
|
|
@ -43,21 +44,6 @@ class WorkSpaceMemberViewSet(BaseViewSet):
|
||||||
serializer_class = WorkspaceMemberAdminSerializer
|
serializer_class = WorkspaceMemberAdminSerializer
|
||||||
model = WorkspaceMember
|
model = WorkspaceMember
|
||||||
|
|
||||||
permission_classes = [
|
|
||||||
WorkspaceEntityPermission,
|
|
||||||
]
|
|
||||||
|
|
||||||
def get_permissions(self):
|
|
||||||
if self.action == "leave":
|
|
||||||
self.permission_classes = [
|
|
||||||
WorkspaceUserPermission,
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
self.permission_classes = [
|
|
||||||
WorkspaceEntityPermission,
|
|
||||||
]
|
|
||||||
|
|
||||||
return super(WorkSpaceMemberViewSet, self).get_permissions()
|
|
||||||
|
|
||||||
search_fields = [
|
search_fields = [
|
||||||
"member__display_name",
|
"member__display_name",
|
||||||
|
|
@ -77,6 +63,9 @@ class WorkSpaceMemberViewSet(BaseViewSet):
|
||||||
)
|
)
|
||||||
|
|
||||||
@cache_response(60 * 60 * 2)
|
@cache_response(60 * 60 * 2)
|
||||||
|
@allow_permission(
|
||||||
|
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE"
|
||||||
|
)
|
||||||
def list(self, request, slug):
|
def list(self, request, slug):
|
||||||
workspace_member = WorkspaceMember.objects.get(
|
workspace_member = WorkspaceMember.objects.get(
|
||||||
member=request.user,
|
member=request.user,
|
||||||
|
|
@ -87,7 +76,7 @@ class WorkSpaceMemberViewSet(BaseViewSet):
|
||||||
# Get all active workspace members
|
# Get all active workspace members
|
||||||
workspace_members = self.get_queryset()
|
workspace_members = self.get_queryset()
|
||||||
|
|
||||||
if workspace_member.role > 10:
|
if workspace_member.role > 5:
|
||||||
serializer = WorkspaceMemberAdminSerializer(
|
serializer = WorkspaceMemberAdminSerializer(
|
||||||
workspace_members,
|
workspace_members,
|
||||||
fields=("id", "member", "role"),
|
fields=("id", "member", "role"),
|
||||||
|
|
@ -107,6 +96,9 @@ class WorkSpaceMemberViewSet(BaseViewSet):
|
||||||
user=False,
|
user=False,
|
||||||
multiple=True,
|
multiple=True,
|
||||||
)
|
)
|
||||||
|
@allow_permission(
|
||||||
|
allowed_roles=[ROLE.ADMIN], level="WORKSPACE"
|
||||||
|
)
|
||||||
def partial_update(self, request, slug, pk):
|
def partial_update(self, request, slug, pk):
|
||||||
workspace_member = WorkspaceMember.objects.get(
|
workspace_member = WorkspaceMember.objects.get(
|
||||||
pk=pk,
|
pk=pk,
|
||||||
|
|
@ -159,6 +151,9 @@ class WorkSpaceMemberViewSet(BaseViewSet):
|
||||||
@invalidate_cache(
|
@invalidate_cache(
|
||||||
path="/api/users/me/workspaces/", user=False, multiple=True
|
path="/api/users/me/workspaces/", user=False, multiple=True
|
||||||
)
|
)
|
||||||
|
@allow_permission(
|
||||||
|
allowed_roles=[ROLE.ADMIN], level="WORKSPACE"
|
||||||
|
)
|
||||||
def destroy(self, request, slug, pk):
|
def destroy(self, request, slug, pk):
|
||||||
# Check the user role who is deleting the user
|
# Check the user role who is deleting the user
|
||||||
workspace_member = WorkspaceMember.objects.get(
|
workspace_member = WorkspaceMember.objects.get(
|
||||||
|
|
@ -233,6 +228,9 @@ class WorkSpaceMemberViewSet(BaseViewSet):
|
||||||
@invalidate_cache(
|
@invalidate_cache(
|
||||||
path="api/users/me/workspaces/", user=False, multiple=True
|
path="api/users/me/workspaces/", user=False, multiple=True
|
||||||
)
|
)
|
||||||
|
@allow_permission(
|
||||||
|
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE"
|
||||||
|
)
|
||||||
def leave(self, request, slug):
|
def leave(self, request, slug):
|
||||||
workspace_member = WorkspaceMember.objects.get(
|
workspace_member = WorkspaceMember.objects.get(
|
||||||
workspace__slug=slug,
|
workspace__slug=slug,
|
||||||
|
|
|
||||||
|
|
@ -288,7 +288,7 @@ class WorkspaceUserProfileEndpoint(BaseAPIView):
|
||||||
is_active=True,
|
is_active=True,
|
||||||
)
|
)
|
||||||
projects = []
|
projects = []
|
||||||
if requesting_workspace_member.role >= 10:
|
if requesting_workspace_member.role >= 15:
|
||||||
projects = (
|
projects = (
|
||||||
Project.objects.filter(
|
Project.objects.filter(
|
||||||
workspace__slug=slug,
|
workspace__slug=slug,
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ def process_workspace_project_invitations(user):
|
||||||
workspace_id=project_member_invite.workspace_id,
|
workspace_id=project_member_invite.workspace_id,
|
||||||
role=(
|
role=(
|
||||||
project_member_invite.role
|
project_member_invite.role
|
||||||
if project_member_invite.role in [5, 10, 15]
|
if project_member_invite.role in [5, 15]
|
||||||
else 15
|
else 15
|
||||||
),
|
),
|
||||||
member=user,
|
member=user,
|
||||||
|
|
@ -67,7 +67,7 @@ def process_workspace_project_invitations(user):
|
||||||
workspace_id=project_member_invite.workspace_id,
|
workspace_id=project_member_invite.workspace_id,
|
||||||
role=(
|
role=(
|
||||||
project_member_invite.role
|
project_member_invite.role
|
||||||
if project_member_invite.role in [5, 10, 15]
|
if project_member_invite.role in [5, 15]
|
||||||
else 15
|
else 15
|
||||||
),
|
),
|
||||||
member=user,
|
member=user,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
# Generated by Django 4.2.15 on 2024-08-30 07:34
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
def update_workspace_project_member_role(apps, schema_editor):
|
||||||
|
WorkspaceMember = apps.get_model("db", "WorkspaceMember")
|
||||||
|
ProjectMember = apps.get_model("db", "ProjectMember")
|
||||||
|
|
||||||
|
# update all existing members with role 10 to role 5
|
||||||
|
WorkspaceMember.objects.filter(role=10).update(role=5)
|
||||||
|
ProjectMember.objects.filter(role=10).update(role=5)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("db", "0075_alter_fileasset_asset"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="projectmember",
|
||||||
|
name="role",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[(20, "Admin"), (15, "Member"), (5, "Guest")],
|
||||||
|
default=5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="projectmemberinvite",
|
||||||
|
name="role",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[(20, "Admin"), (15, "Member"), (5, "Guest")],
|
||||||
|
default=5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="workspacemember",
|
||||||
|
name="role",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[(20, "Admin"), (15, "Member"), (5, "Guest")],
|
||||||
|
default=5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="workspacememberinvite",
|
||||||
|
name="role",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
choices=[(20, "Admin"), (15, "Member"), (5, "Guest")],
|
||||||
|
default=5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="project",
|
||||||
|
name="guest_view_all_features",
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
migrations.RunPython(update_workspace_project_member_role),
|
||||||
|
]
|
||||||
|
|
@ -16,7 +16,6 @@ from .base import BaseModel
|
||||||
ROLE_CHOICES = (
|
ROLE_CHOICES = (
|
||||||
(20, "Admin"),
|
(20, "Admin"),
|
||||||
(15, "Member"),
|
(15, "Member"),
|
||||||
(10, "Viewer"),
|
|
||||||
(5, "Guest"),
|
(5, "Guest"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -98,6 +97,7 @@ class Project(BaseModel):
|
||||||
inbox_view = models.BooleanField(default=False)
|
inbox_view = models.BooleanField(default=False)
|
||||||
is_time_tracking_enabled = models.BooleanField(default=False)
|
is_time_tracking_enabled = models.BooleanField(default=False)
|
||||||
is_issue_type_enabled = models.BooleanField(default=False)
|
is_issue_type_enabled = models.BooleanField(default=False)
|
||||||
|
guest_view_all_features = models.BooleanField(default=False)
|
||||||
cover_image = models.URLField(blank=True, null=True, max_length=800)
|
cover_image = models.URLField(blank=True, null=True, max_length=800)
|
||||||
estimate = models.ForeignKey(
|
estimate = models.ForeignKey(
|
||||||
"db.Estimate",
|
"db.Estimate",
|
||||||
|
|
@ -173,7 +173,7 @@ class ProjectMemberInvite(ProjectBaseModel):
|
||||||
token = models.CharField(max_length=255)
|
token = models.CharField(max_length=255)
|
||||||
message = models.TextField(null=True)
|
message = models.TextField(null=True)
|
||||||
responded_at = models.DateTimeField(null=True)
|
responded_at = models.DateTimeField(null=True)
|
||||||
role = models.PositiveSmallIntegerField(choices=ROLE_CHOICES, default=10)
|
role = models.PositiveSmallIntegerField(choices=ROLE_CHOICES, default=5)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "Project Member Invite"
|
verbose_name = "Project Member Invite"
|
||||||
|
|
@ -194,7 +194,7 @@ class ProjectMember(ProjectBaseModel):
|
||||||
related_name="member_project",
|
related_name="member_project",
|
||||||
)
|
)
|
||||||
comment = models.TextField(blank=True, null=True)
|
comment = models.TextField(blank=True, null=True)
|
||||||
role = models.PositiveSmallIntegerField(choices=ROLE_CHOICES, default=10)
|
role = models.PositiveSmallIntegerField(choices=ROLE_CHOICES, default=5)
|
||||||
view_props = models.JSONField(default=get_default_props)
|
view_props = models.JSONField(default=get_default_props)
|
||||||
default_props = models.JSONField(default=get_default_props)
|
default_props = models.JSONField(default=get_default_props)
|
||||||
preferences = models.JSONField(default=get_default_preferences)
|
preferences = models.JSONField(default=get_default_preferences)
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,8 @@ from .base import BaseModel
|
||||||
from plane.utils.constants import RESTRICTED_WORKSPACE_SLUGS
|
from plane.utils.constants import RESTRICTED_WORKSPACE_SLUGS
|
||||||
|
|
||||||
ROLE_CHOICES = (
|
ROLE_CHOICES = (
|
||||||
(20, "Owner"),
|
(20, "Admin"),
|
||||||
(15, "Admin"),
|
(15, "Member"),
|
||||||
(10, "Member"),
|
|
||||||
(5, "Guest"),
|
(5, "Guest"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -177,7 +176,7 @@ class WorkspaceMember(BaseModel):
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name="member_workspace",
|
related_name="member_workspace",
|
||||||
)
|
)
|
||||||
role = models.PositiveSmallIntegerField(choices=ROLE_CHOICES, default=10)
|
role = models.PositiveSmallIntegerField(choices=ROLE_CHOICES, default=5)
|
||||||
company_role = models.TextField(null=True, blank=True)
|
company_role = models.TextField(null=True, blank=True)
|
||||||
view_props = models.JSONField(default=get_default_props)
|
view_props = models.JSONField(default=get_default_props)
|
||||||
default_props = models.JSONField(default=get_default_props)
|
default_props = models.JSONField(default=get_default_props)
|
||||||
|
|
@ -214,7 +213,7 @@ class WorkspaceMemberInvite(BaseModel):
|
||||||
token = models.CharField(max_length=255)
|
token = models.CharField(max_length=255)
|
||||||
message = models.TextField(null=True)
|
message = models.TextField(null=True)
|
||||||
responded_at = models.DateTimeField(null=True)
|
responded_at = models.DateTimeField(null=True)
|
||||||
role = models.PositiveSmallIntegerField(choices=ROLE_CHOICES, default=10)
|
role = models.PositiveSmallIntegerField(choices=ROLE_CHOICES, default=5)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ["email", "workspace", "deleted_at"]
|
unique_together = ["email", "workspace", "deleted_at"]
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
export enum EUserProjectRoles {
|
export enum EUserPermissions {
|
||||||
GUEST = 5,
|
|
||||||
VIEWER = 10,
|
|
||||||
MEMBER = 15,
|
|
||||||
ADMIN = 20,
|
ADMIN = 20,
|
||||||
|
MEMBER = 15,
|
||||||
|
GUEST = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type TUserPermissions =
|
||||||
|
| EUserPermissions.ADMIN
|
||||||
|
| EUserPermissions.MEMBER
|
||||||
|
| EUserPermissions.GUEST;
|
||||||
|
|
||||||
// project pages
|
// project pages
|
||||||
export enum EPageAccess {
|
export enum EPageAccess {
|
||||||
PUBLIC = 0,
|
PUBLIC = 0,
|
||||||
|
|
|
||||||
11
packages/types/src/project/projects.d.ts
vendored
11
packages/types/src/project/projects.d.ts
vendored
|
|
@ -1,4 +1,3 @@
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
|
||||||
import type {
|
import type {
|
||||||
IProjectViewProps,
|
IProjectViewProps,
|
||||||
IUser,
|
IUser,
|
||||||
|
|
@ -9,6 +8,7 @@ import type {
|
||||||
TLogoProps,
|
TLogoProps,
|
||||||
TStateGroups,
|
TStateGroups,
|
||||||
} from "..";
|
} from "..";
|
||||||
|
import { TUserPermissions } from "../enums";
|
||||||
|
|
||||||
export interface IProject {
|
export interface IProject {
|
||||||
archive_in: number;
|
archive_in: number;
|
||||||
|
|
@ -30,6 +30,7 @@ export interface IProject {
|
||||||
draft_issues: number;
|
draft_issues: number;
|
||||||
draft_sub_issues: number;
|
draft_sub_issues: number;
|
||||||
estimate: string | null;
|
estimate: string | null;
|
||||||
|
guest_view_all_features: boolean;
|
||||||
id: string;
|
id: string;
|
||||||
identifier: string;
|
identifier: string;
|
||||||
anchor: string | null;
|
anchor: string | null;
|
||||||
|
|
@ -38,7 +39,7 @@ export interface IProject {
|
||||||
is_member: boolean;
|
is_member: boolean;
|
||||||
is_time_tracking_enabled: boolean;
|
is_time_tracking_enabled: boolean;
|
||||||
logo_props: TLogoProps;
|
logo_props: TLogoProps;
|
||||||
member_role: EUserProjectRoles | null;
|
member_role: TUserPermissions | null;
|
||||||
members: IProjectMemberLite[];
|
members: IProjectMemberLite[];
|
||||||
name: string;
|
name: string;
|
||||||
network: number;
|
network: number;
|
||||||
|
|
@ -85,7 +86,7 @@ export interface IProjectMember {
|
||||||
project: IProjectLite;
|
project: IProjectLite;
|
||||||
workspace: IWorkspaceLite;
|
workspace: IWorkspaceLite;
|
||||||
comment: string;
|
comment: string;
|
||||||
role: EUserProjectRoles;
|
role: TUserPermissions;
|
||||||
|
|
||||||
preferences: ProjectPreferences;
|
preferences: ProjectPreferences;
|
||||||
|
|
||||||
|
|
@ -101,11 +102,11 @@ export interface IProjectMember {
|
||||||
export interface IProjectMembership {
|
export interface IProjectMembership {
|
||||||
id: string;
|
id: string;
|
||||||
member: string;
|
member: string;
|
||||||
role: EUserProjectRoles;
|
role: TUserPermissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IProjectBulkAddFormData {
|
export interface IProjectBulkAddFormData {
|
||||||
members: { role: EUserProjectRoles; member_id: string }[];
|
members: { role: TUserPermissions; member_id: string }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IGithubRepository {
|
export interface IGithubRepository {
|
||||||
|
|
|
||||||
11
packages/types/src/users.d.ts
vendored
11
packages/types/src/users.d.ts
vendored
|
|
@ -1,9 +1,5 @@
|
||||||
import {
|
import { IIssueActivity, TIssuePriorities, TStateGroups } from ".";
|
||||||
EUserProjectRoles,
|
import { TUserPermissions } from "./enums";
|
||||||
IIssueActivity,
|
|
||||||
TIssuePriorities,
|
|
||||||
TStateGroups,
|
|
||||||
} from ".";
|
|
||||||
|
|
||||||
type TLoginMediums = "email" | "magic-code" | "github" | "gitlab" | "google";
|
type TLoginMediums = "email" | "magic-code" | "github" | "gitlab" | "google";
|
||||||
|
|
||||||
|
|
@ -134,7 +130,6 @@ export interface IUserActivityResponse {
|
||||||
export type UserAuth = {
|
export type UserAuth = {
|
||||||
isMember: boolean;
|
isMember: boolean;
|
||||||
isOwner: boolean;
|
isOwner: boolean;
|
||||||
isViewer: boolean;
|
|
||||||
isGuest: boolean;
|
isGuest: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -175,7 +170,7 @@ export interface IUserProfileProjectSegregation {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUserProjectsRole {
|
export interface IUserProjectsRole {
|
||||||
[projectId: string]: EUserProjectRoles;
|
[projectId: string]: TUserPermissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUserEmailNotificationSettings {
|
export interface IUserEmailNotificationSettings {
|
||||||
|
|
|
||||||
10
packages/types/src/workspace.d.ts
vendored
10
packages/types/src/workspace.d.ts
vendored
|
|
@ -1,4 +1,3 @@
|
||||||
import {EUserWorkspaceRoles} from "@/constants/workspace";
|
|
||||||
import type {
|
import type {
|
||||||
ICycle,
|
ICycle,
|
||||||
IProjectMember,
|
IProjectMember,
|
||||||
|
|
@ -6,6 +5,7 @@ import type {
|
||||||
IUserLite,
|
IUserLite,
|
||||||
IWorkspaceViewProps,
|
IWorkspaceViewProps,
|
||||||
} from "@plane/types";
|
} from "@plane/types";
|
||||||
|
import { TUserPermissions } from "./enums";
|
||||||
|
|
||||||
export interface IWorkspace {
|
export interface IWorkspace {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
|
|
@ -36,7 +36,7 @@ export interface IWorkspaceMemberInvitation {
|
||||||
id: string;
|
id: string;
|
||||||
message: string;
|
message: string;
|
||||||
responded_at: Date;
|
responded_at: Date;
|
||||||
role: EUserWorkspaceRoles;
|
role: TUserPermissions;
|
||||||
token: string;
|
token: string;
|
||||||
workspace: {
|
workspace: {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -47,7 +47,7 @@ export interface IWorkspaceMemberInvitation {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IWorkspaceBulkInviteFormData {
|
export interface IWorkspaceBulkInviteFormData {
|
||||||
emails: {email: string; role: EUserWorkspaceRoles}[];
|
emails: { email: string; role: TUserPermissions }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Properties = {
|
export type Properties = {
|
||||||
|
|
@ -69,7 +69,7 @@ export type Properties = {
|
||||||
export interface IWorkspaceMember {
|
export interface IWorkspaceMember {
|
||||||
id: string;
|
id: string;
|
||||||
member: IUserLite;
|
member: IUserLite;
|
||||||
role: EUserWorkspaceRoles;
|
role: TUserPermissions;
|
||||||
created_at?: string;
|
created_at?: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
|
|
@ -86,7 +86,7 @@ export interface IWorkspaceMemberMe {
|
||||||
default_props: IWorkspaceViewProps;
|
default_props: IWorkspaceViewProps;
|
||||||
id: string;
|
id: string;
|
||||||
member: string;
|
member: string;
|
||||||
role: EUserWorkspaceRoles;
|
role: TUserPermissions;
|
||||||
updated_at: Date;
|
updated_at: Date;
|
||||||
updated_by: string;
|
updated_by: string;
|
||||||
view_props: IWorkspaceViewProps;
|
view_props: IWorkspaceViewProps;
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ const ComboDropDown = forwardRef((props: Props, ref) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
//@ts-ignore
|
// @ts-ignore
|
||||||
<Combobox {...rest} ref={ref}>
|
<Combobox {...rest} ref={ref}>
|
||||||
<Combobox.Button as={Fragment}>{button}</Combobox.Button>
|
<Combobox.Button as={Fragment}>{button}</Combobox.Button>
|
||||||
{children}
|
{children}
|
||||||
|
|
@ -70,4 +70,6 @@ const ComboOptions = Combobox.Options;
|
||||||
const ComboOption = Combobox.Option;
|
const ComboOption = Combobox.Option;
|
||||||
const ComboInput = Combobox.Input;
|
const ComboInput = Combobox.Input;
|
||||||
|
|
||||||
|
ComboDropDown.displayName = "ComboDropDown";
|
||||||
|
|
||||||
export { ComboDropDown, ComboOptions, ComboOption, ComboInput };
|
export { ComboDropDown, ComboOptions, ComboOption, ComboInput };
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import { IssuePeekOverview } from "@/components/issues";
|
||||||
import { EmptyStateType } from "@/constants/empty-state";
|
import { EmptyStateType } from "@/constants/empty-state";
|
||||||
import { ENotificationLoader, ENotificationQueryParamType } from "@/constants/notification";
|
import { ENotificationLoader, ENotificationQueryParamType } from "@/constants/notification";
|
||||||
// hooks
|
// hooks
|
||||||
import { useIssueDetail, useUser, useWorkspace, useWorkspaceNotifications } from "@/hooks/store";
|
import { useIssueDetail, useUserPermissions, useWorkspace, useWorkspaceNotifications } from "@/hooks/store";
|
||||||
import { useWorkspaceIssueProperties } from "@/hooks/use-workspace-issue-properties";
|
import { useWorkspaceIssueProperties } from "@/hooks/use-workspace-issue-properties";
|
||||||
|
|
||||||
const WorkspaceDashboardPage = observer(() => {
|
const WorkspaceDashboardPage = observer(() => {
|
||||||
|
|
@ -28,9 +28,7 @@ const WorkspaceDashboardPage = observer(() => {
|
||||||
notificationIdsByWorkspaceId,
|
notificationIdsByWorkspaceId,
|
||||||
getNotifications,
|
getNotifications,
|
||||||
} = useWorkspaceNotifications();
|
} = useWorkspaceNotifications();
|
||||||
const {
|
const { fetchUserProjectInfo } = useUserPermissions();
|
||||||
membership: { fetchUserProjectInfo },
|
|
||||||
} = useUser();
|
|
||||||
const { setPeekIssue } = useIssueDetail();
|
const { setPeekIssue } = useIssueDetail();
|
||||||
// derived values
|
// derived values
|
||||||
const pageTitle = currentWorkspace?.name ? `${currentWorkspace?.name} - Inbox` : undefined;
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace?.name} - Inbox` : undefined;
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,15 @@
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useParams } from "next/navigation";
|
|
||||||
// ui
|
// ui
|
||||||
import { Button } from "@plane/ui";
|
import { Button } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { PageHead } from "@/components/core";
|
import { PageHead } from "@/components/core";
|
||||||
import { DownloadActivityButton, WorkspaceActivityListPage } from "@/components/profile";
|
import { DownloadActivityButton, WorkspaceActivityListPage } from "@/components/profile";
|
||||||
// constants
|
|
||||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser } from "@/hooks/store";
|
import { useUserPermissions } from "@/hooks/store";
|
||||||
|
// plane-web constants
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
const PER_PAGE = 100;
|
const PER_PAGE = 100;
|
||||||
|
|
||||||
|
|
@ -21,13 +20,7 @@ const ProfileActivityPage = observer(() => {
|
||||||
const [totalPages, setTotalPages] = useState(0);
|
const [totalPages, setTotalPages] = useState(0);
|
||||||
const [resultsCount, setResultsCount] = useState(0);
|
const [resultsCount, setResultsCount] = useState(0);
|
||||||
// router
|
// router
|
||||||
|
const { allowPermissions } = useUserPermissions();
|
||||||
const { userId } = useParams();
|
|
||||||
// store hooks
|
|
||||||
const { data: currentUser } = useUser();
|
|
||||||
const {
|
|
||||||
membership: { currentWorkspaceRole },
|
|
||||||
} = useUser();
|
|
||||||
|
|
||||||
const updateTotalPages = (count: number) => setTotalPages(count);
|
const updateTotalPages = (count: number) => setTotalPages(count);
|
||||||
|
|
||||||
|
|
@ -47,8 +40,10 @@ const ProfileActivityPage = observer(() => {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const canDownloadActivity =
|
const canDownloadActivity = allowPermissions(
|
||||||
currentUser?.id === userId && !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.WORKSPACE
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,9 @@ import { BreadcrumbLink } from "@/components/common";
|
||||||
// components
|
// components
|
||||||
import { ProfileIssuesFilter } from "@/components/profile";
|
import { ProfileIssuesFilter } from "@/components/profile";
|
||||||
import { PROFILE_ADMINS_TAB, PROFILE_VIEWER_TAB } from "@/constants/profile";
|
import { PROFILE_ADMINS_TAB, PROFILE_VIEWER_TAB } from "@/constants/profile";
|
||||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { useAppTheme, useUser } from "@/hooks/store";
|
import { useAppTheme, useUser, useUserPermissions } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
type TUserProfileHeader = {
|
type TUserProfileHeader = {
|
||||||
userProjectsData: IUserProfileProjectSegregation | undefined;
|
userProjectsData: IUserProfileProjectSegregation | undefined;
|
||||||
|
|
@ -28,14 +28,15 @@ export const UserProfileHeader: FC<TUserProfileHeader> = observer((props) => {
|
||||||
const { workspaceSlug, userId } = useParams();
|
const { workspaceSlug, userId } = useParams();
|
||||||
// store hooks
|
// store hooks
|
||||||
const { toggleProfileSidebar, profileSidebarCollapsed } = useAppTheme();
|
const { toggleProfileSidebar, profileSidebarCollapsed } = useAppTheme();
|
||||||
const {
|
const { data: currentUser } = useUser();
|
||||||
membership: { currentWorkspaceRole },
|
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
|
||||||
data: currentUser,
|
|
||||||
} = useUser();
|
|
||||||
// derived values
|
// derived values
|
||||||
const isAuthorized = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.VIEWER;
|
const isAuthorized = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.WORKSPACE
|
||||||
|
);
|
||||||
|
|
||||||
if (!currentWorkspaceRole) return null;
|
if (!workspaceUserInfo) return null;
|
||||||
|
|
||||||
const tabsList = isAuthorized ? [...PROFILE_VIEWER_TAB, ...PROFILE_ADMINS_TAB] : PROFILE_VIEWER_TAB;
|
const tabsList = isAuthorized ? [...PROFILE_VIEWER_TAB, ...PROFILE_ADMINS_TAB] : PROFILE_VIEWER_TAB;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,10 @@ import { ProfileSidebar } from "@/components/profile";
|
||||||
// constants
|
// constants
|
||||||
import { USER_PROFILE_PROJECT_SEGREGATION } from "@/constants/fetch-keys";
|
import { USER_PROFILE_PROJECT_SEGREGATION } from "@/constants/fetch-keys";
|
||||||
import { PROFILE_ADMINS_TAB, PROFILE_VIEWER_TAB } from "@/constants/profile";
|
import { PROFILE_ADMINS_TAB, PROFILE_VIEWER_TAB } from "@/constants/profile";
|
||||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser } from "@/hooks/store";
|
import { useUserPermissions } from "@/hooks/store";
|
||||||
import useSize from "@/hooks/use-window-size";
|
import useSize from "@/hooks/use-window-size";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
// local components
|
// local components
|
||||||
import { UserService } from "@/services/user.service";
|
import { UserService } from "@/services/user.service";
|
||||||
import { UserProfileHeader } from "./header";
|
import { UserProfileHeader } from "./header";
|
||||||
|
|
@ -25,17 +25,18 @@ type Props = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AUTHORIZED_ROLES = [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.VIEWER];
|
|
||||||
|
|
||||||
const UseProfileLayout: React.FC<Props> = observer((props) => {
|
const UseProfileLayout: React.FC<Props> = observer((props) => {
|
||||||
const { children } = props;
|
const { children } = props;
|
||||||
// router
|
// router
|
||||||
const { workspaceSlug, userId } = useParams();
|
const { workspaceSlug, userId } = useParams();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentWorkspaceRole },
|
// derived values
|
||||||
} = useUser();
|
const isAuthorized = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.WORKSPACE
|
||||||
|
);
|
||||||
|
|
||||||
const windowSize = useSize();
|
const windowSize = useSize();
|
||||||
const isSmallerScreen = windowSize[0] >= 768;
|
const isSmallerScreen = windowSize[0] >= 768;
|
||||||
|
|
@ -47,7 +48,6 @@ const UseProfileLayout: React.FC<Props> = observer((props) => {
|
||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
// derived values
|
// derived values
|
||||||
const isAuthorized = currentWorkspaceRole && AUTHORIZED_ROLES.includes(currentWorkspaceRole);
|
|
||||||
const isAuthorizedPath =
|
const isAuthorizedPath =
|
||||||
pathname.includes("assigned") || pathname.includes("created") || pathname.includes("subscribed");
|
pathname.includes("assigned") || pathname.includes("created") || pathname.includes("subscribed");
|
||||||
const isIssuesTab = pathname.includes("assigned") || pathname.includes("created") || pathname.includes("subscribed");
|
const isIssuesTab = pathname.includes("assigned") || pathname.includes("created") || pathname.includes("subscribed");
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import {
|
||||||
EIssuesStoreType,
|
EIssuesStoreType,
|
||||||
ISSUE_DISPLAY_FILTERS_BY_LAYOUT,
|
ISSUE_DISPLAY_FILTERS_BY_LAYOUT,
|
||||||
} from "@/constants/issue";
|
} from "@/constants/issue";
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { isIssueFilterActive } from "@/helpers/filter.helper";
|
import { isIssueFilterActive } from "@/helpers/filter.helper";
|
||||||
|
|
@ -34,13 +33,14 @@ import {
|
||||||
useMember,
|
useMember,
|
||||||
useProject,
|
useProject,
|
||||||
useProjectState,
|
useProjectState,
|
||||||
useUser,
|
|
||||||
useIssues,
|
useIssues,
|
||||||
useCommandPalette,
|
useCommandPalette,
|
||||||
|
useUserPermissions,
|
||||||
} from "@/hooks/store";
|
} from "@/hooks/store";
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
import useLocalStorage from "@/hooks/use-local-storage";
|
import useLocalStorage from "@/hooks/use-local-storage";
|
||||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
const CycleDropdownOption: React.FC<{ cycleId: string }> = ({ cycleId }) => {
|
const CycleDropdownOption: React.FC<{ cycleId: string }> = ({ cycleId }) => {
|
||||||
// router
|
// router
|
||||||
|
|
@ -81,9 +81,6 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
||||||
const { currentProjectCycleIds, getCycleById } = useCycle();
|
const { currentProjectCycleIds, getCycleById } = useCycle();
|
||||||
const { toggleCreateIssueModal } = useCommandPalette();
|
const { toggleCreateIssueModal } = useCommandPalette();
|
||||||
const { setTrackElement } = useEventTracker();
|
const { setTrackElement } = useEventTracker();
|
||||||
const {
|
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const { currentProjectDetails, loader } = useProject();
|
const { currentProjectDetails, loader } = useProject();
|
||||||
const { projectStates } = useProjectState();
|
const { projectStates } = useProjectState();
|
||||||
const { projectLabels } = useLabel();
|
const { projectLabels } = useLabel();
|
||||||
|
|
@ -91,6 +88,7 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
||||||
project: { projectMemberIds },
|
project: { projectMemberIds },
|
||||||
} = useMember();
|
} = useMember();
|
||||||
const { isMobile } = usePlatformOS();
|
const { isMobile } = usePlatformOS();
|
||||||
|
const { allowPermissions } = useUserPermissions();
|
||||||
|
|
||||||
const activeLayout = issueFilters?.displayFilters?.layout;
|
const activeLayout = issueFilters?.displayFilters?.layout;
|
||||||
|
|
||||||
|
|
@ -149,8 +147,10 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
||||||
// derived values
|
// derived values
|
||||||
const cycleDetails = cycleId ? getCycleById(cycleId.toString()) : undefined;
|
const cycleDetails = cycleId ? getCycleById(cycleId.toString()) : undefined;
|
||||||
const isCompletedCycle = cycleDetails?.status?.toLocaleLowerCase() === "completed";
|
const isCompletedCycle = cycleDetails?.status?.toLocaleLowerCase() === "completed";
|
||||||
const canUserCreateIssue =
|
const canUserCreateIssue = allowPermissions(
|
||||||
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.PROJECT
|
||||||
|
);
|
||||||
|
|
||||||
const issuesCount = getGroupIssueCount(undefined, undefined, false);
|
const issuesCount = getGroupIssueCount(undefined, undefined, false);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,11 @@ import { Breadcrumbs, Button, ContrastIcon, Header } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { BreadcrumbLink, Logo } from "@/components/common";
|
import { BreadcrumbLink, Logo } from "@/components/common";
|
||||||
import { CyclesViewHeader } from "@/components/cycles";
|
import { CyclesViewHeader } from "@/components/cycles";
|
||||||
// constants
|
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useCommandPalette, useEventTracker, useProject, useUser } from "@/hooks/store";
|
import { useCommandPalette, useEventTracker, useProject, useUserPermissions } from "@/hooks/store";
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
|
// constants
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
export const CyclesListHeader: FC = observer(() => {
|
export const CyclesListHeader: FC = observer(() => {
|
||||||
// router
|
// router
|
||||||
|
|
@ -21,13 +21,13 @@ export const CyclesListHeader: FC = observer(() => {
|
||||||
// store hooks
|
// store hooks
|
||||||
const { toggleCreateCycleModal } = useCommandPalette();
|
const { toggleCreateCycleModal } = useCommandPalette();
|
||||||
const { setTrackElement } = useEventTracker();
|
const { setTrackElement } = useEventTracker();
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const { currentProjectDetails, loader } = useProject();
|
const { currentProjectDetails, loader } = useProject();
|
||||||
|
|
||||||
const canUserCreateCycle =
|
const canUserCreateCycle = allowPermissions(
|
||||||
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.PROJECT
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Header>
|
<Header>
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,9 @@ import { Breadcrumbs, Button, Intake, Header } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { BreadcrumbLink, Logo } from "@/components/common";
|
import { BreadcrumbLink, Logo } from "@/components/common";
|
||||||
import { InboxIssueCreateEditModalRoot } from "@/components/inbox";
|
import { InboxIssueCreateEditModalRoot } from "@/components/inbox";
|
||||||
// constants
|
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useProject, useProjectInbox, useUser } from "@/hooks/store";
|
import { useProject, useProjectInbox, useUserPermissions } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
export const ProjectInboxHeader: FC = observer(() => {
|
export const ProjectInboxHeader: FC = observer(() => {
|
||||||
// states
|
// states
|
||||||
|
|
@ -20,14 +19,16 @@ export const ProjectInboxHeader: FC = observer(() => {
|
||||||
// router
|
// router
|
||||||
const { workspaceSlug, projectId } = useParams();
|
const { workspaceSlug, projectId } = useParams();
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const { currentProjectDetails, loader: currentProjectDetailsLoader } = useProject();
|
const { currentProjectDetails, loader: currentProjectDetailsLoader } = useProject();
|
||||||
const { loader } = useProjectInbox();
|
const { loader } = useProjectInbox();
|
||||||
|
|
||||||
// derived value
|
// derived value
|
||||||
const isViewer = currentProjectRole === EUserProjectRoles.VIEWER;
|
const isAuthorized = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
||||||
|
EUserPermissionsLevel.PROJECT
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Header>
|
<Header>
|
||||||
|
|
@ -66,7 +67,7 @@ export const ProjectInboxHeader: FC = observer(() => {
|
||||||
</div>
|
</div>
|
||||||
</Header.LeftItem>
|
</Header.LeftItem>
|
||||||
<Header.RightItem>
|
<Header.RightItem>
|
||||||
{currentProjectDetails?.inbox_view && workspaceSlug && projectId && !isViewer ? (
|
{currentProjectDetails?.inbox_view && workspaceSlug && projectId && isAuthorized ? (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<InboxIssueCreateEditModalRoot
|
<InboxIssueCreateEditModalRoot
|
||||||
workspaceSlug={workspaceSlug.toString()}
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
|
|
|
||||||
|
|
@ -12,22 +12,19 @@ import { BreadcrumbLink, CountChip, Logo } from "@/components/common";
|
||||||
import HeaderFilters from "@/components/issues/filters";
|
import HeaderFilters from "@/components/issues/filters";
|
||||||
import { EIssuesStoreType } from "@/constants/issue";
|
import { EIssuesStoreType } from "@/constants/issue";
|
||||||
// helpers
|
// helpers
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
|
||||||
import { SPACE_BASE_PATH, SPACE_BASE_URL } from "@/helpers/common.helper";
|
import { SPACE_BASE_PATH, SPACE_BASE_URL } from "@/helpers/common.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useCommandPalette, useEventTracker, useProject, useUser } from "@/hooks/store";
|
import { useEventTracker, useProject, useCommandPalette, useUserPermissions } from "@/hooks/store";
|
||||||
import { useIssues } from "@/hooks/store/use-issues";
|
import { useIssues } from "@/hooks/store/use-issues";
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
export const ProjectIssuesHeader = observer(() => {
|
export const ProjectIssuesHeader = observer(() => {
|
||||||
// router
|
// router
|
||||||
const router = useAppRouter();
|
const router = useAppRouter();
|
||||||
const { workspaceSlug, projectId } = useParams() as { workspaceSlug: string; projectId: string };
|
const { workspaceSlug, projectId } = useParams() as { workspaceSlug: string; projectId: string };
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const {
|
const {
|
||||||
issues: { getGroupIssueCount },
|
issues: { getGroupIssueCount },
|
||||||
} = useIssues(EIssuesStoreType.PROJECT);
|
} = useIssues(EIssuesStoreType.PROJECT);
|
||||||
|
|
@ -36,14 +33,17 @@ export const ProjectIssuesHeader = observer(() => {
|
||||||
|
|
||||||
const { toggleCreateIssueModal } = useCommandPalette();
|
const { toggleCreateIssueModal } = useCommandPalette();
|
||||||
const { setTrackElement } = useEventTracker();
|
const { setTrackElement } = useEventTracker();
|
||||||
|
const { allowPermissions } = useUserPermissions();
|
||||||
const { isMobile } = usePlatformOS();
|
const { isMobile } = usePlatformOS();
|
||||||
|
|
||||||
const SPACE_APP_URL = (SPACE_BASE_URL.trim() === "" ? window.location.origin : SPACE_BASE_URL) + SPACE_BASE_PATH;
|
const SPACE_APP_URL = (SPACE_BASE_URL.trim() === "" ? window.location.origin : SPACE_BASE_URL) + SPACE_BASE_PATH;
|
||||||
const publishedURL = `${SPACE_APP_URL}/issues/${currentProjectDetails?.anchor}`;
|
const publishedURL = `${SPACE_APP_URL}/issues/${currentProjectDetails?.anchor}`;
|
||||||
|
|
||||||
const issuesCount = getGroupIssueCount(undefined, undefined, false);
|
const issuesCount = getGroupIssueCount(undefined, undefined, false);
|
||||||
const canUserCreateIssue =
|
const canUserCreateIssue = allowPermissions(
|
||||||
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.PROJECT
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Header>
|
<Header>
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import {
|
||||||
EIssueLayoutTypes,
|
EIssueLayoutTypes,
|
||||||
ISSUE_DISPLAY_FILTERS_BY_LAYOUT,
|
ISSUE_DISPLAY_FILTERS_BY_LAYOUT,
|
||||||
} from "@/constants/issue";
|
} from "@/constants/issue";
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { isIssueFilterActive } from "@/helpers/filter.helper";
|
import { isIssueFilterActive } from "@/helpers/filter.helper";
|
||||||
|
|
@ -34,14 +33,15 @@ import {
|
||||||
useModule,
|
useModule,
|
||||||
useProject,
|
useProject,
|
||||||
useProjectState,
|
useProjectState,
|
||||||
useUser,
|
|
||||||
useIssues,
|
useIssues,
|
||||||
useCommandPalette,
|
useCommandPalette,
|
||||||
|
useUserPermissions,
|
||||||
} from "@/hooks/store";
|
} from "@/hooks/store";
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
import { useIssuesActions } from "@/hooks/use-issues-actions";
|
import { useIssuesActions } from "@/hooks/use-issues-actions";
|
||||||
import useLocalStorage from "@/hooks/use-local-storage";
|
import useLocalStorage from "@/hooks/use-local-storage";
|
||||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
const ModuleDropdownOption: React.FC<{ moduleId: string }> = ({ moduleId }) => {
|
const ModuleDropdownOption: React.FC<{ moduleId: string }> = ({ moduleId }) => {
|
||||||
// router
|
// router
|
||||||
|
|
@ -83,9 +83,7 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
||||||
const { projectModuleIds, getModuleById } = useModule();
|
const { projectModuleIds, getModuleById } = useModule();
|
||||||
const { toggleCreateIssueModal } = useCommandPalette();
|
const { toggleCreateIssueModal } = useCommandPalette();
|
||||||
const { setTrackElement } = useEventTracker();
|
const { setTrackElement } = useEventTracker();
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const { currentProjectDetails, loader } = useProject();
|
const { currentProjectDetails, loader } = useProject();
|
||||||
const { projectLabels } = useLabel();
|
const { projectLabels } = useLabel();
|
||||||
const { projectStates } = useProjectState();
|
const { projectStates } = useProjectState();
|
||||||
|
|
@ -149,8 +147,10 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
||||||
|
|
||||||
// derived values
|
// derived values
|
||||||
const moduleDetails = moduleId ? getModuleById(moduleId.toString()) : undefined;
|
const moduleDetails = moduleId ? getModuleById(moduleId.toString()) : undefined;
|
||||||
const canUserCreateIssue =
|
const canUserCreateIssue = allowPermissions(
|
||||||
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.PROJECT
|
||||||
|
);
|
||||||
|
|
||||||
const issuesCount = getGroupIssueCount(undefined, undefined, false);
|
const issuesCount = getGroupIssueCount(undefined, undefined, false);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,11 @@ import { Breadcrumbs, Button, DiceIcon, Header } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { BreadcrumbLink, Logo } from "@/components/common";
|
import { BreadcrumbLink, Logo } from "@/components/common";
|
||||||
import { ModuleViewHeader } from "@/components/modules";
|
import { ModuleViewHeader } from "@/components/modules";
|
||||||
// constants
|
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useCommandPalette, useEventTracker, useProject, useUser } from "@/hooks/store";
|
import { useCommandPalette, useEventTracker, useProject, useUserPermissions } from "@/hooks/store";
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
|
// constants
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
export const ModulesListHeader: React.FC = observer(() => {
|
export const ModulesListHeader: React.FC = observer(() => {
|
||||||
// router
|
// router
|
||||||
|
|
@ -20,14 +20,15 @@ export const ModulesListHeader: React.FC = observer(() => {
|
||||||
// store hooks
|
// store hooks
|
||||||
const { toggleCreateModuleModal } = useCommandPalette();
|
const { toggleCreateModuleModal } = useCommandPalette();
|
||||||
const { setTrackElement } = useEventTracker();
|
const { setTrackElement } = useEventTracker();
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const { currentProjectDetails, loader } = useProject();
|
const { currentProjectDetails, loader } = useProject();
|
||||||
|
|
||||||
// auth
|
// auth
|
||||||
const canUserCreateModule =
|
const canUserCreateModule = allowPermissions(
|
||||||
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.PROJECT
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Header>
|
<Header>
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@ import { Breadcrumbs, Button, Header } from "@plane/ui";
|
||||||
import { BreadcrumbLink, Logo } from "@/components/common";
|
import { BreadcrumbLink, Logo } from "@/components/common";
|
||||||
// constants
|
// constants
|
||||||
import { EPageAccess } from "@/constants/page";
|
import { EPageAccess } from "@/constants/page";
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useCommandPalette, useEventTracker, useProject, useUser } from "@/hooks/store";
|
import { useCommandPalette, useEventTracker, useProject, useUserPermissions } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
export const PagesListHeader = observer(() => {
|
export const PagesListHeader = observer(() => {
|
||||||
// router
|
// router
|
||||||
|
|
@ -20,14 +20,15 @@ export const PagesListHeader = observer(() => {
|
||||||
const pageType = searchParams.get("type");
|
const pageType = searchParams.get("type");
|
||||||
// store hooks
|
// store hooks
|
||||||
const { toggleCreatePageModal } = useCommandPalette();
|
const { toggleCreatePageModal } = useCommandPalette();
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const { currentProjectDetails, loader } = useProject();
|
const { currentProjectDetails, loader } = useProject();
|
||||||
const { setTrackElement } = useEventTracker();
|
const { setTrackElement } = useEventTracker();
|
||||||
|
|
||||||
const canUserCreatePage =
|
const canUserCreatePage = allowPermissions(
|
||||||
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.PROJECT
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Header>
|
<Header>
|
||||||
|
|
|
||||||
|
|
@ -11,18 +11,19 @@ import { NotAuthorizedView } from "@/components/auth-screens";
|
||||||
import { AutoArchiveAutomation, AutoCloseAutomation } from "@/components/automation";
|
import { AutoArchiveAutomation, AutoCloseAutomation } from "@/components/automation";
|
||||||
import { PageHead } from "@/components/core";
|
import { PageHead } from "@/components/core";
|
||||||
// hooks
|
// hooks
|
||||||
import { useProject, useUser } from "@/hooks/store";
|
import { useProject, useUserPermissions } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
const AutomationSettingsPage = observer(() => {
|
const AutomationSettingsPage = observer(() => {
|
||||||
// router
|
// router
|
||||||
const { workspaceSlug, projectId } = useParams();
|
const { workspaceSlug, projectId } = useParams();
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
|
||||||
canPerformProjectAdminActions,
|
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const { currentProjectDetails: projectDetails, updateProject } = useProject();
|
const { currentProjectDetails: projectDetails, updateProject } = useProject();
|
||||||
|
|
||||||
|
// derived values
|
||||||
|
const canPerformProjectAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT);
|
||||||
|
|
||||||
const handleChange = async (formData: Partial<IProject>) => {
|
const handleChange = async (formData: Partial<IProject>) => {
|
||||||
if (!workspaceSlug || !projectId || !projectDetails) return;
|
if (!workspaceSlug || !projectId || !projectDetails) return;
|
||||||
|
|
||||||
|
|
@ -38,7 +39,7 @@ const AutomationSettingsPage = observer(() => {
|
||||||
// derived values
|
// derived values
|
||||||
const pageTitle = projectDetails?.name ? `${projectDetails?.name} - Automations` : undefined;
|
const pageTitle = projectDetails?.name ? `${projectDetails?.name} - Automations` : undefined;
|
||||||
|
|
||||||
if (currentProjectRole && !canPerformProjectAdminActions) {
|
if (workspaceUserInfo && !canPerformProjectAdminActions) {
|
||||||
return <NotAuthorizedView section="settings" isProjectView />;
|
return <NotAuthorizedView section="settings" isProjectView />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,22 +7,22 @@ import { NotAuthorizedView } from "@/components/auth-screens";
|
||||||
import { PageHead } from "@/components/core";
|
import { PageHead } from "@/components/core";
|
||||||
import { EstimateRoot } from "@/components/estimates";
|
import { EstimateRoot } from "@/components/estimates";
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser, useProject } from "@/hooks/store";
|
import { useProject, useUserPermissions } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
const EstimatesSettingsPage = observer(() => {
|
const EstimatesSettingsPage = observer(() => {
|
||||||
const { workspaceSlug, projectId } = useParams();
|
const { workspaceSlug, projectId } = useParams();
|
||||||
const {
|
// store
|
||||||
canPerformProjectAdminActions,
|
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const { currentProjectDetails } = useProject();
|
const { currentProjectDetails } = useProject();
|
||||||
|
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
|
||||||
|
|
||||||
// derived values
|
// derived values
|
||||||
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Estimates` : undefined;
|
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Estimates` : undefined;
|
||||||
|
const canPerformProjectAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT);
|
||||||
|
|
||||||
if (!workspaceSlug || !projectId) return <></>;
|
if (!workspaceSlug || !projectId) return <></>;
|
||||||
|
|
||||||
if (currentProjectRole && !canPerformProjectAdminActions) {
|
if (workspaceUserInfo && !canPerformProjectAdminActions) {
|
||||||
return <NotAuthorizedView section="settings" isProjectView />;
|
return <NotAuthorizedView section="settings" isProjectView />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,22 +7,22 @@ import { NotAuthorizedView } from "@/components/auth-screens";
|
||||||
import { PageHead } from "@/components/core";
|
import { PageHead } from "@/components/core";
|
||||||
import { ProjectFeaturesList } from "@/components/project";
|
import { ProjectFeaturesList } from "@/components/project";
|
||||||
// hooks
|
// hooks
|
||||||
import { useProject, useUser } from "@/hooks/store";
|
import { useProject, useUserPermissions } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
const FeaturesSettingsPage = observer(() => {
|
const FeaturesSettingsPage = observer(() => {
|
||||||
const { workspaceSlug, projectId } = useParams();
|
const { workspaceSlug, projectId } = useParams();
|
||||||
// store
|
// store
|
||||||
const {
|
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
|
||||||
canPerformProjectAdminActions,
|
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const { currentProjectDetails } = useProject();
|
const { currentProjectDetails } = useProject();
|
||||||
// derived values
|
// derived values
|
||||||
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Features` : undefined;
|
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Features` : undefined;
|
||||||
|
const canPerformProjectAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT);
|
||||||
|
|
||||||
if (!workspaceSlug || !projectId) return null;
|
if (!workspaceSlug || !projectId) return null;
|
||||||
|
|
||||||
if (currentProjectRole && !canPerformProjectAdminActions) {
|
if (workspaceUserInfo && !canPerformProjectAdminActions) {
|
||||||
return <NotAuthorizedView section="settings" isProjectView />;
|
return <NotAuthorizedView section="settings" isProjectView />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,25 +9,21 @@ import { Breadcrumbs, CustomMenu, Header } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { BreadcrumbLink, Logo } from "@/components/common";
|
import { BreadcrumbLink, Logo } from "@/components/common";
|
||||||
// constants
|
// constants
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useProject, useUser } from "@/hooks/store";
|
import { useProject, useUserPermissions } from "@/hooks/store";
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
// plane web constants
|
// plane web constants
|
||||||
import { PROJECT_SETTINGS_LINKS } from "@/plane-web/constants/project";
|
import { PROJECT_SETTINGS_LINKS } from "@/plane-web/constants/project";
|
||||||
|
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
export const ProjectSettingHeader: FC = observer(() => {
|
export const ProjectSettingHeader: FC = observer(() => {
|
||||||
// router
|
// router
|
||||||
const router = useAppRouter();
|
const router = useAppRouter();
|
||||||
const { workspaceSlug, projectId } = useParams();
|
const { workspaceSlug, projectId } = useParams();
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const { currentProjectDetails, loader } = useProject();
|
const { currentProjectDetails, loader } = useProject();
|
||||||
|
|
||||||
const projectMemberInfo = currentProjectRole || EUserProjectRoles.GUEST;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Header>
|
<Header>
|
||||||
<Header.LeftItem>
|
<Header.LeftItem>
|
||||||
|
|
@ -74,7 +70,12 @@ export const ProjectSettingHeader: FC = observer(() => {
|
||||||
>
|
>
|
||||||
{PROJECT_SETTINGS_LINKS.map(
|
{PROJECT_SETTINGS_LINKS.map(
|
||||||
(item) =>
|
(item) =>
|
||||||
projectMemberInfo >= item.access && (
|
allowPermissions(
|
||||||
|
item.access,
|
||||||
|
EUserPermissionsLevel.PROJECT,
|
||||||
|
workspaceSlug.toString(),
|
||||||
|
projectId.toString()
|
||||||
|
) && (
|
||||||
<CustomMenu.MenuItem
|
<CustomMenu.MenuItem
|
||||||
key={item.key}
|
key={item.key}
|
||||||
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}${item.href}`)}
|
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}${item.href}`)}
|
||||||
|
|
|
||||||
|
|
@ -9,19 +9,24 @@ import { NotAuthorizedView } from "@/components/auth-screens";
|
||||||
import { PageHead } from "@/components/core";
|
import { PageHead } from "@/components/core";
|
||||||
import { ProjectSettingsLabelList } from "@/components/labels";
|
import { ProjectSettingsLabelList } from "@/components/labels";
|
||||||
// hooks
|
// hooks
|
||||||
import { useProject, useUser } from "@/hooks/store";
|
import { useProject, useUserPermissions } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
const LabelsSettingsPage = observer(() => {
|
const LabelsSettingsPage = observer(() => {
|
||||||
// store hooks
|
// store hooks
|
||||||
const { currentProjectDetails } = useProject();
|
const { currentProjectDetails } = useProject();
|
||||||
const {
|
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
|
||||||
canPerformProjectMemberActions,
|
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Labels` : undefined;
|
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Labels` : undefined;
|
||||||
|
|
||||||
const scrollableContainerRef = useRef<HTMLDivElement | null>(null);
|
const scrollableContainerRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
|
// derived values
|
||||||
|
const canPerformProjectMemberActions = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.PROJECT
|
||||||
|
);
|
||||||
|
|
||||||
// Enable Auto Scroll for Labels list
|
// Enable Auto Scroll for Labels list
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const element = scrollableContainerRef.current;
|
const element = scrollableContainerRef.current;
|
||||||
|
|
@ -35,7 +40,7 @@ const LabelsSettingsPage = observer(() => {
|
||||||
);
|
);
|
||||||
}, [scrollableContainerRef?.current]);
|
}, [scrollableContainerRef?.current]);
|
||||||
|
|
||||||
if (currentProjectRole && !canPerformProjectMemberActions) {
|
if (workspaceUserInfo && !canPerformProjectMemberActions) {
|
||||||
return <NotAuthorizedView section="settings" isProjectView />;
|
return <NotAuthorizedView section="settings" isProjectView />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,19 +6,21 @@ import { NotAuthorizedView } from "@/components/auth-screens";
|
||||||
import { PageHead } from "@/components/core";
|
import { PageHead } from "@/components/core";
|
||||||
import { ProjectMemberList, ProjectSettingsMemberDefaults } from "@/components/project";
|
import { ProjectMemberList, ProjectSettingsMemberDefaults } from "@/components/project";
|
||||||
// hooks
|
// hooks
|
||||||
import { useProject, useUser } from "@/hooks/store";
|
import { useProject, useUserPermissions } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
const MembersSettingsPage = observer(() => {
|
const MembersSettingsPage = observer(() => {
|
||||||
// store
|
// store
|
||||||
const { currentProjectDetails } = useProject();
|
const { currentProjectDetails } = useProject();
|
||||||
const {
|
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
|
||||||
canPerformProjectViewerActions,
|
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
// derived values
|
// derived values
|
||||||
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Members` : undefined;
|
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Members` : undefined;
|
||||||
|
const canPerformProjectMemberActions = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.PROJECT
|
||||||
|
);
|
||||||
|
|
||||||
if (currentProjectRole && !canPerformProjectViewerActions) {
|
if (workspaceUserInfo && !canPerformProjectMemberActions) {
|
||||||
return <NotAuthorizedView section="settings" isProjectView />;
|
return <NotAuthorizedView section="settings" isProjectView />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ import {
|
||||||
ProjectDetailsFormLoader,
|
ProjectDetailsFormLoader,
|
||||||
} from "@/components/project";
|
} from "@/components/project";
|
||||||
// hooks
|
// hooks
|
||||||
import { useProject } from "@/hooks/store";
|
import { useProject, useUserPermissions } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
const GeneralSettingsPage = observer(() => {
|
const GeneralSettingsPage = observer(() => {
|
||||||
// states
|
// states
|
||||||
|
|
@ -25,6 +26,8 @@ const GeneralSettingsPage = observer(() => {
|
||||||
const { workspaceSlug, projectId } = useParams();
|
const { workspaceSlug, projectId } = useParams();
|
||||||
// store hooks
|
// store hooks
|
||||||
const { currentProjectDetails, fetchProjectDetails } = useProject();
|
const { currentProjectDetails, fetchProjectDetails } = useProject();
|
||||||
|
const { allowPermissions } = useUserPermissions();
|
||||||
|
|
||||||
// api call to fetch project details
|
// api call to fetch project details
|
||||||
// TODO: removed this API if not necessary
|
// TODO: removed this API if not necessary
|
||||||
const { isLoading } = useSWR(
|
const { isLoading } = useSWR(
|
||||||
|
|
@ -32,7 +35,13 @@ const GeneralSettingsPage = observer(() => {
|
||||||
workspaceSlug && projectId ? () => fetchProjectDetails(workspaceSlug.toString(), projectId.toString()) : null
|
workspaceSlug && projectId ? () => fetchProjectDetails(workspaceSlug.toString(), projectId.toString()) : null
|
||||||
);
|
);
|
||||||
// derived values
|
// derived values
|
||||||
const isAdmin = currentProjectDetails?.member_role === 20;
|
const isAdmin = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN],
|
||||||
|
EUserPermissionsLevel.PROJECT,
|
||||||
|
workspaceSlug.toString(),
|
||||||
|
projectId.toString()
|
||||||
|
);
|
||||||
|
|
||||||
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - General Settings` : undefined;
|
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - General Settings` : undefined;
|
||||||
// const currentNetwork = NETWORK_CHOICES.find((n) => n.key === projectDetails?.network);
|
// const currentNetwork = NETWORK_CHOICES.find((n) => n.key === projectDetails?.network);
|
||||||
// const selectedNetwork = NETWORK_CHOICES.find((n) => n.key === watch("network"));
|
// const selectedNetwork = NETWORK_CHOICES.find((n) => n.key === watch("network"));
|
||||||
|
|
@ -69,7 +78,7 @@ const GeneralSettingsPage = observer(() => {
|
||||||
<ProjectDetailsFormLoader />
|
<ProjectDetailsFormLoader />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isAdmin && (
|
{isAdmin && currentProjectDetails && (
|
||||||
<>
|
<>
|
||||||
<ArchiveProjectSelection
|
<ArchiveProjectSelection
|
||||||
projectDetails={currentProjectDetails}
|
projectDetails={currentProjectDetails}
|
||||||
|
|
|
||||||
|
|
@ -8,22 +8,20 @@ import { useParams, usePathname } from "next/navigation";
|
||||||
import { Loader } from "@plane/ui";
|
import { Loader } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { SidebarNavItem } from "@/components/sidebar";
|
import { SidebarNavItem } from "@/components/sidebar";
|
||||||
// constants
|
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser } from "@/hooks/store";
|
import { useUserPermissions } from "@/hooks/store";
|
||||||
// plane web constants
|
// plane web constants
|
||||||
import { PROJECT_SETTINGS_LINKS } from "@/plane-web/constants/project";
|
import { PROJECT_SETTINGS_LINKS } from "@/plane-web/constants/project";
|
||||||
|
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
export const ProjectSettingsSidebar = observer(() => {
|
export const ProjectSettingsSidebar = observer(() => {
|
||||||
const { workspaceSlug, projectId } = useParams();
|
const { workspaceSlug, projectId } = useParams();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
// mobx store
|
// mobx store
|
||||||
const {
|
const { allowPermissions, projectUserInfo } = useUserPermissions();
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
|
|
||||||
const projectMemberInfo = currentProjectRole || EUserProjectRoles.GUEST;
|
// derived values
|
||||||
|
const currentProjectRole = projectUserInfo?.[workspaceSlug?.toString()]?.[projectId?.toString()]?.role;
|
||||||
|
|
||||||
if (!currentProjectRole) {
|
if (!currentProjectRole) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -47,7 +45,12 @@ export const ProjectSettingsSidebar = observer(() => {
|
||||||
<div className="flex w-full flex-col gap-1">
|
<div className="flex w-full flex-col gap-1">
|
||||||
{PROJECT_SETTINGS_LINKS.map(
|
{PROJECT_SETTINGS_LINKS.map(
|
||||||
(link) =>
|
(link) =>
|
||||||
projectMemberInfo >= link.access && (
|
allowPermissions(
|
||||||
|
link.access,
|
||||||
|
EUserPermissionsLevel.PROJECT,
|
||||||
|
workspaceSlug.toString(),
|
||||||
|
projectId.toString()
|
||||||
|
) && (
|
||||||
<Link key={link.key} href={`/${workspaceSlug}/projects/${projectId}${link.href}`}>
|
<Link key={link.key} href={`/${workspaceSlug}/projects/${projectId}${link.href}`}>
|
||||||
<SidebarNavItem
|
<SidebarNavItem
|
||||||
key={link.key}
|
key={link.key}
|
||||||
|
|
|
||||||
|
|
@ -7,20 +7,24 @@ import { NotAuthorizedView } from "@/components/auth-screens";
|
||||||
import { PageHead } from "@/components/core";
|
import { PageHead } from "@/components/core";
|
||||||
import { ProjectStateRoot } from "@/components/project-states";
|
import { ProjectStateRoot } from "@/components/project-states";
|
||||||
// hook
|
// hook
|
||||||
import { useProject, useUser } from "@/hooks/store";
|
import { useProject, useUserPermissions } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
const StatesSettingsPage = observer(() => {
|
const StatesSettingsPage = observer(() => {
|
||||||
const { workspaceSlug, projectId } = useParams();
|
const { workspaceSlug, projectId } = useParams();
|
||||||
// store
|
// store
|
||||||
const { currentProjectDetails } = useProject();
|
const { currentProjectDetails } = useProject();
|
||||||
const {
|
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
|
||||||
canPerformProjectMemberActions,
|
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
// derived values
|
// derived values
|
||||||
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - States` : undefined;
|
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - States` : undefined;
|
||||||
|
// derived values
|
||||||
|
const canPerformProjectMemberActions = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.PROJECT
|
||||||
|
);
|
||||||
|
|
||||||
if (currentProjectRole && !canPerformProjectMemberActions) {
|
if (workspaceUserInfo && !canPerformProjectMemberActions) {
|
||||||
return <NotAuthorizedView section="settings" isProjectView />;
|
return <NotAuthorizedView section="settings" isProjectView />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ import {
|
||||||
EIssueLayoutTypes,
|
EIssueLayoutTypes,
|
||||||
ISSUE_DISPLAY_FILTERS_BY_LAYOUT,
|
ISSUE_DISPLAY_FILTERS_BY_LAYOUT,
|
||||||
} from "@/constants/issue";
|
} from "@/constants/issue";
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
|
||||||
import { EViewAccess } from "@/constants/views";
|
import { EViewAccess } from "@/constants/views";
|
||||||
// helpers
|
// helpers
|
||||||
import { isIssueFilterActive } from "@/helpers/filter.helper";
|
import { isIssueFilterActive } from "@/helpers/filter.helper";
|
||||||
|
|
@ -35,8 +34,9 @@ import {
|
||||||
useProject,
|
useProject,
|
||||||
useProjectState,
|
useProjectState,
|
||||||
useProjectView,
|
useProjectView,
|
||||||
useUser,
|
useUserPermissions,
|
||||||
} from "@/hooks/store";
|
} from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
||||||
// router
|
// router
|
||||||
|
|
@ -47,9 +47,8 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
||||||
} = useIssues(EIssuesStoreType.PROJECT_VIEW);
|
} = useIssues(EIssuesStoreType.PROJECT_VIEW);
|
||||||
const { setTrackElement } = useEventTracker();
|
const { setTrackElement } = useEventTracker();
|
||||||
const { toggleCreateIssueModal } = useCommandPalette();
|
const { toggleCreateIssueModal } = useCommandPalette();
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const { currentProjectDetails, loader } = useProject();
|
const { currentProjectDetails, loader } = useProject();
|
||||||
const { projectViewIds, getViewById } = useProjectView();
|
const { projectViewIds, getViewById } = useProjectView();
|
||||||
const { projectStates } = useProjectState();
|
const { projectStates } = useProjectState();
|
||||||
|
|
@ -131,8 +130,10 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
||||||
|
|
||||||
const viewDetails = viewId ? getViewById(viewId.toString()) : null;
|
const viewDetails = viewId ? getViewById(viewId.toString()) : null;
|
||||||
|
|
||||||
const canUserCreateIssue =
|
const canUserCreateIssue = allowPermissions(
|
||||||
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.PROJECT
|
||||||
|
);
|
||||||
const publishLink = getPublishViewLink(viewDetails?.anchor);
|
const publishLink = getPublishViewLink(viewDetails?.anchor);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@ import { APITokenSettingsLoader } from "@/components/ui";
|
||||||
import { EmptyStateType } from "@/constants/empty-state";
|
import { EmptyStateType } from "@/constants/empty-state";
|
||||||
import { API_TOKENS_LIST } from "@/constants/fetch-keys";
|
import { API_TOKENS_LIST } from "@/constants/fetch-keys";
|
||||||
// store hooks
|
// store hooks
|
||||||
import { useUser, useWorkspace } from "@/hooks/store";
|
import { useUserPermissions, useWorkspace } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
// services
|
// services
|
||||||
import { APITokenService } from "@/services/api_token.service";
|
import { APITokenService } from "@/services/api_token.service";
|
||||||
|
|
||||||
|
|
@ -28,11 +29,10 @@ const ApiTokensPage = observer(() => {
|
||||||
// router
|
// router
|
||||||
const { workspaceSlug } = useParams();
|
const { workspaceSlug } = useParams();
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
|
||||||
canPerformWorkspaceAdminActions,
|
|
||||||
membership: { currentWorkspaceRole },
|
|
||||||
} = useUser();
|
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
|
||||||
|
// derived values
|
||||||
|
const canPerformWorkspaceAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||||
|
|
||||||
const { data: tokens } = useSWR(
|
const { data: tokens } = useSWR(
|
||||||
workspaceSlug && canPerformWorkspaceAdminActions ? API_TOKENS_LIST(workspaceSlug.toString()) : null,
|
workspaceSlug && canPerformWorkspaceAdminActions ? API_TOKENS_LIST(workspaceSlug.toString()) : null,
|
||||||
|
|
@ -42,7 +42,7 @@ const ApiTokensPage = observer(() => {
|
||||||
|
|
||||||
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - API Tokens` : undefined;
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - API Tokens` : undefined;
|
||||||
|
|
||||||
if (currentWorkspaceRole && !canPerformWorkspaceAdminActions) {
|
if (workspaceUserInfo && !canPerformWorkspaceAdminActions) {
|
||||||
return <NotAuthorizedView section="settings" />;
|
return <NotAuthorizedView section="settings" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,21 +5,20 @@ import { observer } from "mobx-react";
|
||||||
import { NotAuthorizedView } from "@/components/auth-screens";
|
import { NotAuthorizedView } from "@/components/auth-screens";
|
||||||
import { PageHead } from "@/components/core";
|
import { PageHead } from "@/components/core";
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser, useWorkspace } from "@/hooks/store";
|
import { useUserPermissions, useWorkspace } from "@/hooks/store";
|
||||||
// plane web components
|
// plane web components
|
||||||
import { BillingRoot } from "@/plane-web/components/workspace";
|
import { BillingRoot } from "@/plane-web/components/workspace";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
const BillingSettingsPage = observer(() => {
|
const BillingSettingsPage = observer(() => {
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
|
||||||
canPerformWorkspaceAdminActions,
|
|
||||||
membership: { currentWorkspaceRole },
|
|
||||||
} = useUser();
|
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
// derived values
|
// derived values
|
||||||
|
const canPerformWorkspaceAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||||
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Billing & Plans` : undefined;
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Billing & Plans` : undefined;
|
||||||
|
|
||||||
if (currentWorkspaceRole && !canPerformWorkspaceAdminActions) {
|
if (workspaceUserInfo && !canPerformWorkspaceAdminActions) {
|
||||||
return <NotAuthorizedView section="settings" />;
|
return <NotAuthorizedView section="settings" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,22 +8,23 @@ import ExportGuide from "@/components/exporter/guide";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser, useWorkspace } from "@/hooks/store";
|
import { useUserPermissions, useWorkspace } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
const ExportsPage = observer(() => {
|
const ExportsPage = observer(() => {
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
|
||||||
canPerformWorkspaceViewerActions,
|
|
||||||
canPerformWorkspaceMemberActions,
|
|
||||||
membership: { currentWorkspaceRole },
|
|
||||||
} = useUser();
|
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|
||||||
// derived values
|
// derived values
|
||||||
|
const canPerformWorkspaceMemberActions = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.WORKSPACE
|
||||||
|
);
|
||||||
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Exports` : undefined;
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Exports` : undefined;
|
||||||
|
|
||||||
// if user is not authorized to view this page
|
// if user is not authorized to view this page
|
||||||
if (currentWorkspaceRole && !canPerformWorkspaceViewerActions) {
|
if (workspaceUserInfo && !canPerformWorkspaceMemberActions) {
|
||||||
return <NotAuthorizedView section="settings" />;
|
return <NotAuthorizedView section="settings" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,20 +4,17 @@ import { observer } from "mobx-react";
|
||||||
// components
|
// components
|
||||||
import { PageHead } from "@/components/core";
|
import { PageHead } from "@/components/core";
|
||||||
import IntegrationGuide from "@/components/integration/guide";
|
import IntegrationGuide from "@/components/integration/guide";
|
||||||
// constants
|
|
||||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser, useWorkspace } from "@/hooks/store";
|
import { useUserPermissions, useWorkspace } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
const ImportsPage = observer(() => {
|
const ImportsPage = observer(() => {
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
|
||||||
membership: { currentWorkspaceRole },
|
|
||||||
} = useUser();
|
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
const { allowPermissions } = useUserPermissions();
|
||||||
|
|
||||||
// derived values
|
// derived values
|
||||||
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||||
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Imports` : undefined;
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Imports` : undefined;
|
||||||
|
|
||||||
if (!isAdmin)
|
if (!isAdmin)
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,9 @@ import { SingleIntegrationCard } from "@/components/integration";
|
||||||
import { IntegrationAndImportExportBanner, IntegrationsSettingsLoader } from "@/components/ui";
|
import { IntegrationAndImportExportBanner, IntegrationsSettingsLoader } from "@/components/ui";
|
||||||
// constants
|
// constants
|
||||||
import { APP_INTEGRATIONS } from "@/constants/fetch-keys";
|
import { APP_INTEGRATIONS } from "@/constants/fetch-keys";
|
||||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser, useWorkspace } from "@/hooks/store";
|
import { useUserPermissions, useWorkspace } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
// services
|
// services
|
||||||
import { IntegrationService } from "@/services/integrations";
|
import { IntegrationService } from "@/services/integrations";
|
||||||
|
|
||||||
|
|
@ -20,13 +20,11 @@ const WorkspaceIntegrationsPage = observer(() => {
|
||||||
// router
|
// router
|
||||||
const { workspaceSlug } = useParams();
|
const { workspaceSlug } = useParams();
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
|
||||||
membership: { currentWorkspaceRole },
|
|
||||||
} = useUser();
|
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
const { allowPermissions } = useUserPermissions();
|
||||||
|
|
||||||
// derived values
|
// derived values
|
||||||
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||||
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Integrations` : undefined;
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Integrations` : undefined;
|
||||||
|
|
||||||
if (!isAdmin)
|
if (!isAdmin)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,8 @@ import { MEMBER_INVITED } from "@/constants/event-tracker";
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { getUserRole } from "@/helpers/user.helper";
|
import { getUserRole } from "@/helpers/user.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useEventTracker, useMember, useUser, useWorkspace } from "@/hooks/store";
|
import { useEventTracker, useMember, useUserPermissions, useWorkspace } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
const WorkspaceMembersSettingsPage = observer(() => {
|
const WorkspaceMembersSettingsPage = observer(() => {
|
||||||
// states
|
// states
|
||||||
|
|
@ -27,18 +28,20 @@ const WorkspaceMembersSettingsPage = observer(() => {
|
||||||
// router
|
// router
|
||||||
const { workspaceSlug } = useParams();
|
const { workspaceSlug } = useParams();
|
||||||
// store hooks
|
// store hooks
|
||||||
|
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
|
||||||
const { captureEvent } = useEventTracker();
|
const { captureEvent } = useEventTracker();
|
||||||
const {
|
|
||||||
canPerformWorkspaceAdminActions,
|
|
||||||
canPerformWorkspaceViewerActions,
|
|
||||||
canPerformWorkspaceMemberActions,
|
|
||||||
membership: { currentWorkspaceRole },
|
|
||||||
} = useUser();
|
|
||||||
const {
|
const {
|
||||||
workspace: { inviteMembersToWorkspace },
|
workspace: { inviteMembersToWorkspace },
|
||||||
} = useMember();
|
} = useMember();
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|
||||||
|
// derived values
|
||||||
|
const canPerformWorkspaceAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||||
|
const canPerformWorkspaceMemberActions = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.WORKSPACE
|
||||||
|
);
|
||||||
|
|
||||||
const handleWorkspaceInvite = (data: IWorkspaceBulkInviteFormData) => {
|
const handleWorkspaceInvite = (data: IWorkspaceBulkInviteFormData) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
|
|
@ -49,7 +52,7 @@ const WorkspaceMembersSettingsPage = observer(() => {
|
||||||
emails: [
|
emails: [
|
||||||
...data.emails.map((email) => ({
|
...data.emails.map((email) => ({
|
||||||
email: email.email,
|
email: email.email,
|
||||||
role: getUserRole(email.role),
|
role: getUserRole(email.role as unknown as EUserPermissions),
|
||||||
})),
|
})),
|
||||||
],
|
],
|
||||||
project_id: undefined,
|
project_id: undefined,
|
||||||
|
|
@ -67,7 +70,7 @@ const WorkspaceMembersSettingsPage = observer(() => {
|
||||||
emails: [
|
emails: [
|
||||||
...data.emails.map((email) => ({
|
...data.emails.map((email) => ({
|
||||||
email: email.email,
|
email: email.email,
|
||||||
role: getUserRole(email.role),
|
role: getUserRole(email.role as unknown as EUserPermissions),
|
||||||
})),
|
})),
|
||||||
],
|
],
|
||||||
project_id: undefined,
|
project_id: undefined,
|
||||||
|
|
@ -86,7 +89,7 @@ const WorkspaceMembersSettingsPage = observer(() => {
|
||||||
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Members` : undefined;
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Members` : undefined;
|
||||||
|
|
||||||
// if user is not authorized to view this page
|
// if user is not authorized to view this page
|
||||||
if (currentWorkspaceRole && !canPerformWorkspaceViewerActions) {
|
if (workspaceUserInfo && !canPerformWorkspaceMemberActions) {
|
||||||
return <NotAuthorizedView section="settings" />;
|
return <NotAuthorizedView section="settings" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useParams, usePathname } from "next/navigation";
|
import { useParams, usePathname } from "next/navigation";
|
||||||
// constants
|
|
||||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser } from "@/hooks/store";
|
import { useUserPermissions } from "@/hooks/store";
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
// plane web constants
|
// plane web constants
|
||||||
|
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
import { WORKSPACE_SETTINGS_LINKS } from "@/plane-web/constants/workspace";
|
import { WORKSPACE_SETTINGS_LINKS } from "@/plane-web/constants/workspace";
|
||||||
// plane web helpers
|
// plane web helpers
|
||||||
import { shouldRenderSettingLink } from "@/plane-web/helpers/workspace.helper";
|
import { shouldRenderSettingLink } from "@/plane-web/helpers/workspace.helper";
|
||||||
|
|
@ -15,19 +14,14 @@ export const MobileWorkspaceSettingsTabs = observer(() => {
|
||||||
const { workspaceSlug } = useParams();
|
const { workspaceSlug } = useParams();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
// mobx store
|
// mobx store
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentWorkspaceRole },
|
|
||||||
} = useUser();
|
|
||||||
|
|
||||||
// derived values
|
|
||||||
const workspaceMemberInfo = currentWorkspaceRole || EUserWorkspaceRoles.GUEST;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-shrink-0 md:hidden sticky inset-0 flex overflow-x-auto bg-custom-background-100 z-10">
|
<div className="flex-shrink-0 md:hidden sticky inset-0 flex overflow-x-auto bg-custom-background-100 z-10">
|
||||||
{WORKSPACE_SETTINGS_LINKS.map(
|
{WORKSPACE_SETTINGS_LINKS.map(
|
||||||
(item, index) =>
|
(item, index) =>
|
||||||
shouldRenderSettingLink(item.key) &&
|
shouldRenderSettingLink(item.key) &&
|
||||||
workspaceMemberInfo >= item.access && (
|
allowPermissions(item.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString()) && (
|
||||||
<div
|
<div
|
||||||
className={`${
|
className={`${
|
||||||
item.highlight(pathname, `/${workspaceSlug}`)
|
item.highlight(pathname, `/${workspaceSlug}`)
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,10 @@ import Link from "next/link";
|
||||||
import { useParams, usePathname } from "next/navigation";
|
import { useParams, usePathname } from "next/navigation";
|
||||||
// components
|
// components
|
||||||
import { SidebarNavItem } from "@/components/sidebar";
|
import { SidebarNavItem } from "@/components/sidebar";
|
||||||
// constants
|
|
||||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser } from "@/hooks/store";
|
import { useUserPermissions } from "@/hooks/store";
|
||||||
// plane web constants
|
// plane web constants
|
||||||
|
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
import { WORKSPACE_SETTINGS_LINKS } from "@/plane-web/constants/workspace";
|
import { WORKSPACE_SETTINGS_LINKS } from "@/plane-web/constants/workspace";
|
||||||
// plane web helpers
|
// plane web helpers
|
||||||
import { shouldRenderSettingLink } from "@/plane-web/helpers/workspace.helper";
|
import { shouldRenderSettingLink } from "@/plane-web/helpers/workspace.helper";
|
||||||
|
|
@ -20,12 +19,7 @@ export const WorkspaceSettingsSidebar = observer(() => {
|
||||||
const { workspaceSlug } = useParams();
|
const { workspaceSlug } = useParams();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
// mobx store
|
// mobx store
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentWorkspaceRole },
|
|
||||||
} = useUser();
|
|
||||||
|
|
||||||
// derived values
|
|
||||||
const workspaceMemberInfo = currentWorkspaceRole || EUserWorkspaceRoles.GUEST;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex w-[280px] flex-col gap-6">
|
<div className="flex w-[280px] flex-col gap-6">
|
||||||
|
|
@ -35,7 +29,7 @@ export const WorkspaceSettingsSidebar = observer(() => {
|
||||||
{WORKSPACE_SETTINGS_LINKS.map(
|
{WORKSPACE_SETTINGS_LINKS.map(
|
||||||
(link) =>
|
(link) =>
|
||||||
shouldRenderSettingLink(link.key) &&
|
shouldRenderSettingLink(link.key) &&
|
||||||
workspaceMemberInfo >= link.access && (
|
allowPermissions(link.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString()) && (
|
||||||
<Link key={link.key} href={`/${workspaceSlug}${link.href}`}>
|
<Link key={link.key} href={`/${workspaceSlug}${link.href}`}>
|
||||||
<SidebarNavItem
|
<SidebarNavItem
|
||||||
key={link.key}
|
key={link.key}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ import { LogoSpinner } from "@/components/common";
|
||||||
import { PageHead } from "@/components/core";
|
import { PageHead } from "@/components/core";
|
||||||
import { DeleteWebhookModal, WebhookDeleteSection, WebhookForm } from "@/components/web-hooks";
|
import { DeleteWebhookModal, WebhookDeleteSection, WebhookForm } from "@/components/web-hooks";
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser, useWebhook, useWorkspace } from "@/hooks/store";
|
import { useUserPermissions, useWebhook, useWorkspace } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
const WebhookDetailsPage = observer(() => {
|
const WebhookDetailsPage = observer(() => {
|
||||||
// states
|
// states
|
||||||
|
|
@ -20,18 +21,17 @@ const WebhookDetailsPage = observer(() => {
|
||||||
// router
|
// router
|
||||||
const { workspaceSlug, webhookId } = useParams();
|
const { workspaceSlug, webhookId } = useParams();
|
||||||
// mobx store
|
// mobx store
|
||||||
const {
|
|
||||||
membership: { currentWorkspaceRole },
|
|
||||||
} = useUser();
|
|
||||||
const { currentWebhook, fetchWebhookById, updateWebhook } = useWebhook();
|
const { currentWebhook, fetchWebhookById, updateWebhook } = useWebhook();
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
const { allowPermissions } = useUserPermissions();
|
||||||
|
|
||||||
// TODO: fix this error
|
// TODO: fix this error
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// if (isCreated !== "true") clearSecretKey();
|
// if (isCreated !== "true") clearSecretKey();
|
||||||
// }, [clearSecretKey, isCreated]);
|
// }, [clearSecretKey, isCreated]);
|
||||||
|
|
||||||
const isAdmin = currentWorkspaceRole === 20;
|
// derived values
|
||||||
|
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||||
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Webhook` : undefined;
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Webhook` : undefined;
|
||||||
|
|
||||||
useSWR(
|
useSWR(
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ import { WebhooksList, CreateWebhookModal } from "@/components/web-hooks";
|
||||||
// constants
|
// constants
|
||||||
import { EmptyStateType } from "@/constants/empty-state";
|
import { EmptyStateType } from "@/constants/empty-state";
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser, useWebhook, useWorkspace } from "@/hooks/store";
|
import { useUserPermissions, useWebhook, useWorkspace } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
const WebhooksListPage = observer(() => {
|
const WebhooksListPage = observer(() => {
|
||||||
// states
|
// states
|
||||||
|
|
@ -23,13 +24,13 @@ const WebhooksListPage = observer(() => {
|
||||||
// router
|
// router
|
||||||
const { workspaceSlug } = useParams();
|
const { workspaceSlug } = useParams();
|
||||||
// mobx store
|
// mobx store
|
||||||
const {
|
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
|
||||||
canPerformWorkspaceAdminActions,
|
|
||||||
membership: { currentWorkspaceRole },
|
|
||||||
} = useUser();
|
|
||||||
const { fetchWebhooks, webhooks, clearSecretKey, webhookSecretKey, createWebhook } = useWebhook();
|
const { fetchWebhooks, webhooks, clearSecretKey, webhookSecretKey, createWebhook } = useWebhook();
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|
||||||
|
const canPerformWorkspaceAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||||
|
|
||||||
useSWR(
|
useSWR(
|
||||||
workspaceSlug && canPerformWorkspaceAdminActions ? `WEBHOOKS_LIST_${workspaceSlug}` : null,
|
workspaceSlug && canPerformWorkspaceAdminActions ? `WEBHOOKS_LIST_${workspaceSlug}` : null,
|
||||||
workspaceSlug && canPerformWorkspaceAdminActions ? () => fetchWebhooks(workspaceSlug.toString()) : null
|
workspaceSlug && canPerformWorkspaceAdminActions ? () => fetchWebhooks(workspaceSlug.toString()) : null
|
||||||
|
|
@ -42,7 +43,7 @@ const WebhooksListPage = observer(() => {
|
||||||
if (!showCreateWebhookModal && webhookSecretKey) clearSecretKey();
|
if (!showCreateWebhookModal && webhookSecretKey) clearSecretKey();
|
||||||
}, [showCreateWebhookModal, webhookSecretKey, clearSecretKey]);
|
}, [showCreateWebhookModal, webhookSecretKey, clearSecretKey]);
|
||||||
|
|
||||||
if (currentWorkspaceRole && !canPerformWorkspaceAdminActions) {
|
if (workspaceUserInfo && !canPerformWorkspaceAdminActions) {
|
||||||
return <NotAuthorizedView section="settings" />;
|
return <NotAuthorizedView section="settings" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,22 +13,29 @@ import {
|
||||||
import { SidebarFavoritesMenu } from "@/components/workspace/sidebar/favorites/favorites-menu";
|
import { SidebarFavoritesMenu } from "@/components/workspace/sidebar/favorites/favorites-menu";
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useAppTheme, useUser } from "@/hooks/store";
|
import { useAppTheme, useUserPermissions } from "@/hooks/store";
|
||||||
import useOutsideClickDetector from "@/hooks/use-outside-click-detector";
|
import useOutsideClickDetector from "@/hooks/use-outside-click-detector";
|
||||||
// plane web components
|
// plane web components
|
||||||
import useSize from "@/hooks/use-window-size";
|
import useSize from "@/hooks/use-window-size";
|
||||||
import { SidebarAppSwitcher } from "@/plane-web/components/sidebar";
|
import { SidebarAppSwitcher } from "@/plane-web/components/sidebar";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
export interface IAppSidebar {}
|
export interface IAppSidebar {}
|
||||||
|
|
||||||
export const AppSidebar: FC<IAppSidebar> = observer(() => {
|
export const AppSidebar: FC<IAppSidebar> = observer(() => {
|
||||||
// store hooks
|
// store hooks
|
||||||
const { canPerformWorkspaceMemberActions } = useUser();
|
const { allowPermissions } = useUserPermissions();
|
||||||
const { toggleSidebar, sidebarCollapsed } = useAppTheme();
|
const { toggleSidebar, sidebarCollapsed } = useAppTheme();
|
||||||
const windowSize = useSize();
|
const windowSize = useSize();
|
||||||
// refs
|
// refs
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
// derived values
|
||||||
|
const canPerformWorkspaceMemberActions = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.WORKSPACE
|
||||||
|
);
|
||||||
|
|
||||||
useOutsideClickDetector(ref, () => {
|
useOutsideClickDetector(ref, () => {
|
||||||
if (sidebarCollapsed === false) {
|
if (sidebarCollapsed === false) {
|
||||||
if (window.innerWidth < 768) {
|
if (window.innerWidth < 768) {
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,9 @@ import { useEventTracker, useUser, useUserProfile, useWorkspace } from "@/hooks/
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
// services
|
// services
|
||||||
import { AuthenticationWrapper } from "@/lib/wrappers";
|
import { AuthenticationWrapper } from "@/lib/wrappers";
|
||||||
|
// plane web constants
|
||||||
|
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
|
||||||
|
// plane web services
|
||||||
import { WorkspaceService } from "@/plane-web/services";
|
import { WorkspaceService } from "@/plane-web/services";
|
||||||
// images
|
// images
|
||||||
import emptyInvitation from "@/public/empty-state/invitation.svg";
|
import emptyInvitation from "@/public/empty-state/invitation.svg";
|
||||||
|
|
@ -88,7 +91,7 @@ const UserInvitationsPage = observer(() => {
|
||||||
captureEvent(MEMBER_ACCEPTED, {
|
captureEvent(MEMBER_ACCEPTED, {
|
||||||
member_id: invitation?.id,
|
member_id: invitation?.id,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
|
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
|
||||||
role: getUserRole(invitation?.role!),
|
role: getUserRole((invitation?.role as unknown as EUserPermissions)!),
|
||||||
project_id: undefined,
|
project_id: undefined,
|
||||||
accepted_from: "App",
|
accepted_from: "App",
|
||||||
state: "SUCCESS",
|
state: "SUCCESS",
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { IWorkspaceMember } from "@plane/types";
|
import { IWorkspaceMember } from "@plane/types";
|
||||||
import { EUserProjectRoles } from "@plane/types/src/enums";
|
|
||||||
import { AccountTypeColumn, NameColumn } from "@/components/project/settings/member-columns";
|
import { AccountTypeColumn, NameColumn } from "@/components/project/settings/member-columns";
|
||||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
import { useUser, useUserPermissions } from "@/hooks/store";
|
||||||
import { useUser } from "@/hooks/store";
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
export interface RowData {
|
export interface RowData {
|
||||||
member: IWorkspaceMember;
|
member: IWorkspaceMember;
|
||||||
role: EUserWorkspaceRoles;
|
role: EUserPermissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useProjectColumns = () => {
|
export const useProjectColumns = () => {
|
||||||
|
|
@ -17,10 +16,12 @@ export const useProjectColumns = () => {
|
||||||
|
|
||||||
const { workspaceSlug, projectId } = useParams();
|
const { workspaceSlug, projectId } = useParams();
|
||||||
|
|
||||||
const {
|
const { data: currentUser } = useUser();
|
||||||
membership: { currentProjectRole },
|
const { allowPermissions, projectUserInfo } = useUserPermissions();
|
||||||
data: currentUser,
|
|
||||||
} = useUser();
|
const currentProjectRole =
|
||||||
|
(projectUserInfo?.[workspaceSlug.toString()]?.[projectId.toString()]?.role as unknown as EUserPermissions) ??
|
||||||
|
EUserPermissions.GUEST;
|
||||||
|
|
||||||
const getFormattedDate = (dateStr: string) => {
|
const getFormattedDate = (dateStr: string) => {
|
||||||
const date = new Date(dateStr);
|
const date = new Date(dateStr);
|
||||||
|
|
@ -29,7 +30,13 @@ export const useProjectColumns = () => {
|
||||||
return date.toLocaleDateString("en-US", options);
|
return date.toLocaleDateString("en-US", options);
|
||||||
};
|
};
|
||||||
// derived values
|
// derived values
|
||||||
const isAdmin = currentProjectRole === EUserProjectRoles.ADMIN;
|
const isAdmin = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN],
|
||||||
|
EUserPermissionsLevel.PROJECT,
|
||||||
|
workspaceSlug.toString(),
|
||||||
|
projectId.toString()
|
||||||
|
);
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
key: "Full Name",
|
key: "Full Name",
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { AccountTypeColumn, NameColumn, RowData } from "@/components/workspace/settings/member-columns";
|
import { AccountTypeColumn, NameColumn, RowData } from "@/components/workspace/settings/member-columns";
|
||||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
import { useUser, useUserPermissions } from "@/hooks/store";
|
||||||
import { useUser } from "@/hooks/store";
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
export const useMemberColumns = () => {
|
export const useMemberColumns = () => {
|
||||||
// states
|
// states
|
||||||
|
|
@ -10,10 +10,8 @@ export const useMemberColumns = () => {
|
||||||
|
|
||||||
const { workspaceSlug } = useParams();
|
const { workspaceSlug } = useParams();
|
||||||
|
|
||||||
const {
|
const { data: currentUser } = useUser();
|
||||||
membership: { currentWorkspaceRole },
|
const { allowPermissions } = useUserPermissions();
|
||||||
data: currentUser,
|
|
||||||
} = useUser();
|
|
||||||
|
|
||||||
const getFormattedDate = (dateStr: string) => {
|
const getFormattedDate = (dateStr: string) => {
|
||||||
const date = new Date(dateStr);
|
const date = new Date(dateStr);
|
||||||
|
|
@ -22,7 +20,8 @@ export const useMemberColumns = () => {
|
||||||
return date.toLocaleDateString("en-US", options);
|
return date.toLocaleDateString("en-US", options);
|
||||||
};
|
};
|
||||||
|
|
||||||
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
// derived values
|
||||||
|
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
|
|
@ -48,13 +47,7 @@ export const useMemberColumns = () => {
|
||||||
{
|
{
|
||||||
key: "Account Type",
|
key: "Account Type",
|
||||||
content: "Account Type",
|
content: "Account Type",
|
||||||
tdRender: (rowData: RowData) => (
|
tdRender: (rowData: RowData) => <AccountTypeColumn rowData={rowData} workspaceSlug={workspaceSlug as string} />,
|
||||||
<AccountTypeColumn
|
|
||||||
rowData={rowData}
|
|
||||||
currentWorkspaceRole={currentWorkspaceRole}
|
|
||||||
workspaceSlug={workspaceSlug as string}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,14 @@ import { SettingIcon } from "@/components/icons/attachment";
|
||||||
// types
|
// types
|
||||||
import { Props } from "@/components/icons/types";
|
import { Props } from "@/components/icons/types";
|
||||||
// constants
|
// constants
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
import { EUserPermissions } from "../../user-permissions";
|
||||||
|
|
||||||
export const PROJECT_SETTINGS = {
|
export const PROJECT_SETTINGS = {
|
||||||
general: {
|
general: {
|
||||||
key: "general",
|
key: "general",
|
||||||
label: "General",
|
label: "General",
|
||||||
href: `/settings`,
|
href: `/settings`,
|
||||||
access: EUserProjectRoles.GUEST,
|
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
||||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/`,
|
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/`,
|
||||||
Icon: SettingIcon,
|
Icon: SettingIcon,
|
||||||
},
|
},
|
||||||
|
|
@ -18,7 +18,7 @@ export const PROJECT_SETTINGS = {
|
||||||
key: "members",
|
key: "members",
|
||||||
label: "Members",
|
label: "Members",
|
||||||
href: `/settings/members`,
|
href: `/settings/members`,
|
||||||
access: EUserProjectRoles.VIEWER,
|
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/members/`,
|
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/members/`,
|
||||||
Icon: SettingIcon,
|
Icon: SettingIcon,
|
||||||
},
|
},
|
||||||
|
|
@ -26,7 +26,7 @@ export const PROJECT_SETTINGS = {
|
||||||
key: "features",
|
key: "features",
|
||||||
label: "Features",
|
label: "Features",
|
||||||
href: `/settings/features`,
|
href: `/settings/features`,
|
||||||
access: EUserProjectRoles.ADMIN,
|
access: [EUserPermissions.ADMIN],
|
||||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/features/`,
|
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/features/`,
|
||||||
Icon: SettingIcon,
|
Icon: SettingIcon,
|
||||||
},
|
},
|
||||||
|
|
@ -34,7 +34,7 @@ export const PROJECT_SETTINGS = {
|
||||||
key: "states",
|
key: "states",
|
||||||
label: "States",
|
label: "States",
|
||||||
href: `/settings/states`,
|
href: `/settings/states`,
|
||||||
access: EUserProjectRoles.MEMBER,
|
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/states/`,
|
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/states/`,
|
||||||
Icon: SettingIcon,
|
Icon: SettingIcon,
|
||||||
},
|
},
|
||||||
|
|
@ -42,7 +42,7 @@ export const PROJECT_SETTINGS = {
|
||||||
key: "labels",
|
key: "labels",
|
||||||
label: "Labels",
|
label: "Labels",
|
||||||
href: `/settings/labels`,
|
href: `/settings/labels`,
|
||||||
access: EUserProjectRoles.MEMBER,
|
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/labels/`,
|
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/labels/`,
|
||||||
Icon: SettingIcon,
|
Icon: SettingIcon,
|
||||||
},
|
},
|
||||||
|
|
@ -50,7 +50,7 @@ export const PROJECT_SETTINGS = {
|
||||||
key: "estimates",
|
key: "estimates",
|
||||||
label: "Estimates",
|
label: "Estimates",
|
||||||
href: `/settings/estimates`,
|
href: `/settings/estimates`,
|
||||||
access: EUserProjectRoles.ADMIN,
|
access: [EUserPermissions.ADMIN],
|
||||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/estimates/`,
|
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/estimates/`,
|
||||||
Icon: SettingIcon,
|
Icon: SettingIcon,
|
||||||
},
|
},
|
||||||
|
|
@ -58,7 +58,7 @@ export const PROJECT_SETTINGS = {
|
||||||
key: "automations",
|
key: "automations",
|
||||||
label: "Automations",
|
label: "Automations",
|
||||||
href: `/settings/automations`,
|
href: `/settings/automations`,
|
||||||
access: EUserProjectRoles.ADMIN,
|
access: [EUserPermissions.ADMIN],
|
||||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/automations/`,
|
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/automations/`,
|
||||||
Icon: SettingIcon,
|
Icon: SettingIcon,
|
||||||
},
|
},
|
||||||
|
|
@ -68,7 +68,7 @@ export const PROJECT_SETTINGS_LINKS: {
|
||||||
key: string;
|
key: string;
|
||||||
label: string;
|
label: string;
|
||||||
href: string;
|
href: string;
|
||||||
access: EUserProjectRoles;
|
access: EUserPermissions[];
|
||||||
highlight: (pathname: string, baseUrl: string) => boolean;
|
highlight: (pathname: string, baseUrl: string) => boolean;
|
||||||
Icon: React.FC<Props>;
|
Icon: React.FC<Props>;
|
||||||
}[] = [
|
}[] = [
|
||||||
|
|
|
||||||
36
web/ce/constants/user-permissions/index.ts
Normal file
36
web/ce/constants/user-permissions/index.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
export enum EUserPermissionsLevel {
|
||||||
|
WORKSPACE = "WORKSPACE",
|
||||||
|
PROJECT = "PROJECT",
|
||||||
|
}
|
||||||
|
export type TUserPermissionsLevel = EUserPermissionsLevel;
|
||||||
|
|
||||||
|
export enum EUserPermissions {
|
||||||
|
ADMIN = 20,
|
||||||
|
MEMBER = 15,
|
||||||
|
GUEST = 5,
|
||||||
|
}
|
||||||
|
export type TUserPermissions = EUserPermissions;
|
||||||
|
|
||||||
|
export type TUserAllowedPermissionsObject = {
|
||||||
|
create: TUserPermissions[];
|
||||||
|
update: TUserPermissions[];
|
||||||
|
delete: TUserPermissions[];
|
||||||
|
read: TUserPermissions[];
|
||||||
|
};
|
||||||
|
export type TUserAllowedPermissions = {
|
||||||
|
workspace: {
|
||||||
|
[key: string]: Partial<TUserAllowedPermissionsObject>;
|
||||||
|
};
|
||||||
|
project: {
|
||||||
|
[key: string]: Partial<TUserAllowedPermissionsObject>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const USER_ALLOWED_PERMISSIONS: TUserAllowedPermissions = {
|
||||||
|
workspace: {
|
||||||
|
dashboard: {
|
||||||
|
read: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
project: {},
|
||||||
|
};
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
// icons
|
// icons
|
||||||
import { SettingIcon } from "@/components/icons/attachment";
|
import { SettingIcon } from "@/components/icons/attachment";
|
||||||
import { Props } from "@/components/icons/types";
|
import { Props } from "@/components/icons/types";
|
||||||
|
import { EUserPermissions } from "./user-permissions";
|
||||||
// constants
|
// constants
|
||||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
|
||||||
|
|
||||||
export const WORKSPACE_SETTINGS = {
|
export const WORKSPACE_SETTINGS = {
|
||||||
general: {
|
general: {
|
||||||
key: "general",
|
key: "general",
|
||||||
label: "General",
|
label: "General",
|
||||||
href: `/settings`,
|
href: `/settings`,
|
||||||
access: EUserWorkspaceRoles.GUEST,
|
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
||||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/`,
|
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/`,
|
||||||
Icon: SettingIcon,
|
Icon: SettingIcon,
|
||||||
},
|
},
|
||||||
|
|
@ -17,7 +17,7 @@ export const WORKSPACE_SETTINGS = {
|
||||||
key: "members",
|
key: "members",
|
||||||
label: "Members",
|
label: "Members",
|
||||||
href: `/settings/members`,
|
href: `/settings/members`,
|
||||||
access: EUserWorkspaceRoles.VIEWER,
|
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/members/`,
|
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/members/`,
|
||||||
Icon: SettingIcon,
|
Icon: SettingIcon,
|
||||||
},
|
},
|
||||||
|
|
@ -25,7 +25,7 @@ export const WORKSPACE_SETTINGS = {
|
||||||
key: "billing-and-plans",
|
key: "billing-and-plans",
|
||||||
label: "Billing and plans",
|
label: "Billing and plans",
|
||||||
href: `/settings/billing`,
|
href: `/settings/billing`,
|
||||||
access: EUserWorkspaceRoles.ADMIN,
|
access: [EUserPermissions.ADMIN],
|
||||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/billing/`,
|
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/billing/`,
|
||||||
Icon: SettingIcon,
|
Icon: SettingIcon,
|
||||||
},
|
},
|
||||||
|
|
@ -33,7 +33,7 @@ export const WORKSPACE_SETTINGS = {
|
||||||
key: "export",
|
key: "export",
|
||||||
label: "Exports",
|
label: "Exports",
|
||||||
href: `/settings/exports`,
|
href: `/settings/exports`,
|
||||||
access: EUserWorkspaceRoles.VIEWER,
|
access: [EUserPermissions.ADMIN],
|
||||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/exports/`,
|
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/exports/`,
|
||||||
Icon: SettingIcon,
|
Icon: SettingIcon,
|
||||||
},
|
},
|
||||||
|
|
@ -41,7 +41,7 @@ export const WORKSPACE_SETTINGS = {
|
||||||
key: "webhooks",
|
key: "webhooks",
|
||||||
label: "Webhooks",
|
label: "Webhooks",
|
||||||
href: `/settings/webhooks`,
|
href: `/settings/webhooks`,
|
||||||
access: EUserWorkspaceRoles.ADMIN,
|
access: [EUserPermissions.ADMIN],
|
||||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/webhooks/`,
|
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/webhooks/`,
|
||||||
Icon: SettingIcon,
|
Icon: SettingIcon,
|
||||||
},
|
},
|
||||||
|
|
@ -49,7 +49,7 @@ export const WORKSPACE_SETTINGS = {
|
||||||
key: "api-tokens",
|
key: "api-tokens",
|
||||||
label: "API tokens",
|
label: "API tokens",
|
||||||
href: `/settings/api-tokens`,
|
href: `/settings/api-tokens`,
|
||||||
access: EUserWorkspaceRoles.ADMIN,
|
access: [EUserPermissions.ADMIN],
|
||||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/api-tokens/`,
|
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/api-tokens/`,
|
||||||
Icon: SettingIcon,
|
Icon: SettingIcon,
|
||||||
},
|
},
|
||||||
|
|
@ -59,7 +59,7 @@ export const WORKSPACE_SETTINGS_LINKS: {
|
||||||
key: string;
|
key: string;
|
||||||
label: string;
|
label: string;
|
||||||
href: string;
|
href: string;
|
||||||
access: EUserWorkspaceRoles;
|
access: EUserPermissions[];
|
||||||
highlight: (pathname: string, baseUrl: string) => boolean;
|
highlight: (pathname: string, baseUrl: string) => boolean;
|
||||||
Icon: React.FC<Props>;
|
Icon: React.FC<Props>;
|
||||||
}[] = [
|
}[] = [
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { useParams } from "next/navigation";
|
||||||
// hooks
|
// hooks
|
||||||
import { ClipboardList } from "lucide-react";
|
import { ClipboardList } from "lucide-react";
|
||||||
import { Button } from "@plane/ui";
|
import { Button } from "@plane/ui";
|
||||||
import { useProject, useUser } from "@/hooks/store";
|
import { useProject, useUserPermissions } from "@/hooks/store";
|
||||||
// ui
|
// ui
|
||||||
// icons
|
// icons
|
||||||
// images
|
// images
|
||||||
|
|
@ -15,9 +15,7 @@ export const JoinProject: React.FC = () => {
|
||||||
// states
|
// states
|
||||||
const [isJoiningProject, setIsJoiningProject] = useState(false);
|
const [isJoiningProject, setIsJoiningProject] = useState(false);
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const { joinProject } = useUserPermissions();
|
||||||
membership: { joinProject },
|
|
||||||
} = useUser();
|
|
||||||
const { fetchProjects } = useProject();
|
const { fetchProjects } = useProject();
|
||||||
|
|
||||||
const { workspaceSlug, projectId } = useParams();
|
const { workspaceSlug, projectId } = useParams();
|
||||||
|
|
@ -27,7 +25,7 @@ export const JoinProject: React.FC = () => {
|
||||||
|
|
||||||
setIsJoiningProject(true);
|
setIsJoiningProject(true);
|
||||||
|
|
||||||
joinProject(workspaceSlug.toString(), [projectId.toString()])
|
joinProject(workspaceSlug.toString(), projectId.toString())
|
||||||
.then(() => fetchProjects(workspaceSlug.toString()))
|
.then(() => fetchProjects(workspaceSlug.toString()))
|
||||||
.finally(() => setIsJoiningProject(false));
|
.finally(() => setIsJoiningProject(false));
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,10 @@ import { CustomSelect, Loader, ToggleSwitch } from "@plane/ui";
|
||||||
// component
|
// component
|
||||||
import { SelectMonthModal } from "@/components/automation";
|
import { SelectMonthModal } from "@/components/automation";
|
||||||
// constants
|
// constants
|
||||||
import { EUserProjectRoles, PROJECT_AUTOMATION_MONTHS } from "@/constants/project";
|
import { PROJECT_AUTOMATION_MONTHS } from "@/constants/project";
|
||||||
// hooks
|
// hooks
|
||||||
import { useProject, useUser } from "@/hooks/store";
|
import { useProject, useUserPermissions } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
handleChange: (formData: Partial<IProject>) => Promise<void>;
|
handleChange: (formData: Partial<IProject>) => Promise<void>;
|
||||||
|
|
@ -25,12 +26,16 @@ export const AutoArchiveAutomation: React.FC<Props> = observer((props) => {
|
||||||
// states
|
// states
|
||||||
const [monthModal, setmonthModal] = useState(false);
|
const [monthModal, setmonthModal] = useState(false);
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const { currentProjectDetails } = useProject();
|
const { currentProjectDetails } = useProject();
|
||||||
|
|
||||||
const isAdmin = currentProjectRole === EUserProjectRoles.ADMIN;
|
const isAdmin = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN],
|
||||||
|
EUserPermissionsLevel.PROJECT,
|
||||||
|
currentProjectDetails?.workspace_detail.slug,
|
||||||
|
currentProjectDetails?.id
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,10 @@ import { CustomSelect, CustomSearchSelect, ToggleSwitch, StateGroupIcon, DoubleC
|
||||||
// component
|
// component
|
||||||
import { SelectMonthModal } from "@/components/automation";
|
import { SelectMonthModal } from "@/components/automation";
|
||||||
// constants
|
// constants
|
||||||
import { EUserProjectRoles, PROJECT_AUTOMATION_MONTHS } from "@/constants/project";
|
import { PROJECT_AUTOMATION_MONTHS } from "@/constants/project";
|
||||||
// hooks
|
// hooks
|
||||||
import { useProject, useProjectState, useUser } from "@/hooks/store";
|
import { useProject, useProjectState, useUserPermissions } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
handleChange: (formData: Partial<IProject>) => Promise<void>;
|
handleChange: (formData: Partial<IProject>) => Promise<void>;
|
||||||
|
|
@ -24,11 +25,9 @@ export const AutoCloseAutomation: React.FC<Props> = observer((props) => {
|
||||||
// states
|
// states
|
||||||
const [monthModal, setmonthModal] = useState(false);
|
const [monthModal, setmonthModal] = useState(false);
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const { currentProjectDetails } = useProject();
|
const { currentProjectDetails } = useProject();
|
||||||
const { projectStates } = useProjectState();
|
const { projectStates } = useProjectState();
|
||||||
|
const { allowPermissions } = useUserPermissions();
|
||||||
|
|
||||||
// const stateGroups = projectStateStore.groupedProjectStates ?? undefined;
|
// const stateGroups = projectStateStore.groupedProjectStates ?? undefined;
|
||||||
|
|
||||||
|
|
@ -57,7 +56,12 @@ export const AutoCloseAutomation: React.FC<Props> = observer((props) => {
|
||||||
default_state: defaultState,
|
default_state: defaultState,
|
||||||
};
|
};
|
||||||
|
|
||||||
const isAdmin = currentProjectRole === EUserProjectRoles.ADMIN;
|
const isAdmin = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN],
|
||||||
|
EUserPermissionsLevel.PROJECT,
|
||||||
|
currentProjectDetails?.workspace_detail?.slug,
|
||||||
|
currentProjectDetails?.id
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@ import { Command } from "cmdk";
|
||||||
// hooks
|
// hooks
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
// constants
|
|
||||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser } from "@/hooks/store";
|
import { useUserPermissions } from "@/hooks/store";
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
// plane wev constants
|
// plane wev constants
|
||||||
|
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
import { WORKSPACE_SETTINGS_LINKS } from "@/plane-web/constants/workspace";
|
import { WORKSPACE_SETTINGS_LINKS } from "@/plane-web/constants/workspace";
|
||||||
// plane web helpers
|
// plane web helpers
|
||||||
import { shouldRenderSettingLink } from "@/plane-web/helpers/workspace.helper";
|
import { shouldRenderSettingLink } from "@/plane-web/helpers/workspace.helper";
|
||||||
|
|
@ -25,11 +25,8 @@ export const CommandPaletteWorkspaceSettingsActions: React.FC<Props> = (props) =
|
||||||
// router params
|
// router params
|
||||||
const { workspaceSlug } = useParams();
|
const { workspaceSlug } = useParams();
|
||||||
// mobx store
|
// mobx store
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentWorkspaceRole },
|
|
||||||
} = useUser();
|
|
||||||
// derived values
|
// derived values
|
||||||
const workspaceMemberInfo = currentWorkspaceRole || EUserWorkspaceRoles.GUEST;
|
|
||||||
|
|
||||||
const redirect = (path: string) => {
|
const redirect = (path: string) => {
|
||||||
closePalette();
|
closePalette();
|
||||||
|
|
@ -40,7 +37,7 @@ export const CommandPaletteWorkspaceSettingsActions: React.FC<Props> = (props) =
|
||||||
<>
|
<>
|
||||||
{WORKSPACE_SETTINGS_LINKS.map(
|
{WORKSPACE_SETTINGS_LINKS.map(
|
||||||
(setting) =>
|
(setting) =>
|
||||||
workspaceMemberInfo >= setting.access &&
|
allowPermissions(setting.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString()) &&
|
||||||
shouldRenderSettingLink(setting.key) && (
|
shouldRenderSettingLink(setting.key) && (
|
||||||
<Command.Item
|
<Command.Item
|
||||||
key={setting.key}
|
key={setting.key}
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,11 @@ import { ISSUE_DETAILS } from "@/constants/fetch-keys";
|
||||||
// helpers
|
// helpers
|
||||||
import { copyTextToClipboard } from "@/helpers/string.helper";
|
import { copyTextToClipboard } from "@/helpers/string.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useEventTracker, useUser, useAppTheme, useCommandPalette } from "@/hooks/store";
|
import { useEventTracker, useUser, useAppTheme, useCommandPalette, useUserPermissions } from "@/hooks/store";
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
import { useIssuesStore } from "@/hooks/use-issue-layout-store";
|
import { useIssuesStore } from "@/hooks/use-issue-layout-store";
|
||||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
// services
|
// services
|
||||||
import { IssueService } from "@/services/issue";
|
import { IssueService } from "@/services/issue";
|
||||||
|
|
||||||
|
|
@ -43,10 +44,10 @@ export const CommandPalette: FC = observer(() => {
|
||||||
const { platform } = usePlatformOS();
|
const { platform } = usePlatformOS();
|
||||||
const {
|
const {
|
||||||
data: currentUser,
|
data: currentUser,
|
||||||
canPerformProjectMemberActions,
|
// canPerformProjectMemberActions,
|
||||||
canPerformWorkspaceMemberActions,
|
// canPerformWorkspaceMemberActions,
|
||||||
canPerformAnyCreateAction,
|
canPerformAnyCreateAction,
|
||||||
canPerformProjectAdminActions,
|
// canPerformProjectAdminActions,
|
||||||
} = useUser();
|
} = useUser();
|
||||||
const {
|
const {
|
||||||
issues: { removeIssue },
|
issues: { removeIssue },
|
||||||
|
|
@ -73,6 +74,7 @@ export const CommandPalette: FC = observer(() => {
|
||||||
toggleDeleteIssueModal,
|
toggleDeleteIssueModal,
|
||||||
isAnyModalOpen,
|
isAnyModalOpen,
|
||||||
} = useCommandPalette();
|
} = useCommandPalette();
|
||||||
|
const { allowPermissions } = useUserPermissions();
|
||||||
|
|
||||||
const { data: issueDetails } = useSWR(
|
const { data: issueDetails } = useSWR(
|
||||||
workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null,
|
workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null,
|
||||||
|
|
@ -81,6 +83,17 @@ export const CommandPalette: FC = observer(() => {
|
||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// derived values
|
||||||
|
const canPerformWorkspaceMemberActions = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.WORKSPACE
|
||||||
|
);
|
||||||
|
const canPerformProjectMemberActions = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.PROJECT
|
||||||
|
);
|
||||||
|
const canPerformProjectAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT);
|
||||||
|
|
||||||
const copyIssueUrlToClipboard = useCallback(() => {
|
const copyIssueUrlToClipboard = useCallback(() => {
|
||||||
if (!issueId) return;
|
if (!issueId) return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,16 +17,16 @@ import { DateRangeDropdown } from "@/components/dropdowns";
|
||||||
// constants
|
// constants
|
||||||
import { CYCLE_STATUS } from "@/constants/cycle";
|
import { CYCLE_STATUS } from "@/constants/cycle";
|
||||||
import { CYCLE_UPDATED } from "@/constants/event-tracker";
|
import { CYCLE_UPDATED } from "@/constants/event-tracker";
|
||||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
|
||||||
// helpers
|
// helpers
|
||||||
import { findHowManyDaysLeft, getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
import { findHowManyDaysLeft, getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
||||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useEventTracker, useCycle, useUser, useMember, useProjectEstimates } from "@/hooks/store";
|
import { useEventTracker, useCycle, useMember, useProjectEstimates, useUserPermissions } from "@/hooks/store";
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
// plane web constants
|
// plane web constants
|
||||||
import { EEstimateSystem } from "@/plane-web/constants/estimates";
|
import { EEstimateSystem } from "@/plane-web/constants/estimates";
|
||||||
// services
|
// services
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
import { CycleService } from "@/services/cycle.service";
|
import { CycleService } from "@/services/cycle.service";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
@ -55,9 +55,8 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||||
// store hooks
|
// store hooks
|
||||||
const { setTrackElement, captureCycleEvent } = useEventTracker();
|
const { setTrackElement, captureCycleEvent } = useEventTracker();
|
||||||
const { areEstimateEnabledByProjectId, currentActiveEstimateId, estimateById } = useProjectEstimates();
|
const { areEstimateEnabledByProjectId, currentActiveEstimateId, estimateById } = useProjectEstimates();
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const { getCycleById, updateCycleDetails, restoreCycle } = useCycle();
|
const { getCycleById, updateCycleDetails, restoreCycle } = useCycle();
|
||||||
const { getUserDetails } = useMember();
|
const { getUserDetails } = useMember();
|
||||||
// derived values
|
// derived values
|
||||||
|
|
@ -236,7 +235,10 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||||
|
|
||||||
const daysLeft = findHowManyDaysLeft(cycleDetails.end_date);
|
const daysLeft = findHowManyDaysLeft(cycleDetails.end_date);
|
||||||
|
|
||||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
|
const isEditingAllowed = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.PROJECT
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,10 @@ import { TCycleFilters } from "@plane/types";
|
||||||
// hooks
|
// hooks
|
||||||
import { Tag } from "@plane/ui";
|
import { Tag } from "@plane/ui";
|
||||||
import { AppliedDateFilters, AppliedStatusFilters } from "@/components/cycles";
|
import { AppliedDateFilters, AppliedStatusFilters } from "@/components/cycles";
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
|
||||||
import { replaceUnderscoreIfSnakeCase } from "@/helpers/string.helper";
|
import { replaceUnderscoreIfSnakeCase } from "@/helpers/string.helper";
|
||||||
import { useUser } from "@/hooks/store";
|
import { useUserPermissions } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
// components
|
// components
|
||||||
// helpers
|
// helpers
|
||||||
// types
|
// types
|
||||||
|
|
@ -24,15 +25,15 @@ const DATE_FILTERS = ["start_date", "end_date"];
|
||||||
export const CycleAppliedFiltersList: React.FC<Props> = observer((props) => {
|
export const CycleAppliedFiltersList: React.FC<Props> = observer((props) => {
|
||||||
const { appliedFilters, handleClearAllFilters, handleRemoveFilter, alwaysAllowEditing } = props;
|
const { appliedFilters, handleClearAllFilters, handleRemoveFilter, alwaysAllowEditing } = props;
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
|
|
||||||
if (!appliedFilters) return null;
|
if (!appliedFilters) return null;
|
||||||
|
|
||||||
if (Object.keys(appliedFilters).length === 0) return null;
|
if (Object.keys(appliedFilters).length === 0) return null;
|
||||||
|
|
||||||
const isEditingAllowed = alwaysAllowEditing || (currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER);
|
const isEditingAllowed =
|
||||||
|
alwaysAllowEditing ||
|
||||||
|
allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-wrap items-stretch gap-2 bg-custom-background-100">
|
<div className="flex flex-wrap items-stretch gap-2 bg-custom-background-100">
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,14 @@ import { CycleQuickActions } from "@/components/cycles";
|
||||||
// constants
|
// constants
|
||||||
import { CYCLE_STATUS } from "@/constants/cycle";
|
import { CYCLE_STATUS } from "@/constants/cycle";
|
||||||
import { CYCLE_FAVORITED, CYCLE_UNFAVORITED } from "@/constants/event-tracker";
|
import { CYCLE_FAVORITED, CYCLE_UNFAVORITED } from "@/constants/event-tracker";
|
||||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
|
||||||
// helpers
|
// helpers
|
||||||
import { findHowManyDaysLeft, getDate, renderFormattedDate } from "@/helpers/date-time.helper";
|
import { findHowManyDaysLeft, getDate, renderFormattedDate } from "@/helpers/date-time.helper";
|
||||||
import { generateQueryParams } from "@/helpers/router.helper";
|
import { generateQueryParams } from "@/helpers/router.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useEventTracker, useCycle, useUser, useMember } from "@/hooks/store";
|
import { useEventTracker, useCycle, useMember, useUserPermissions } from "@/hooks/store";
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
export interface ICyclesBoardCard {
|
export interface ICyclesBoardCard {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
|
|
@ -39,9 +39,8 @@ export const CyclesBoardCard: FC<ICyclesBoardCard> = observer((props) => {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
// store
|
// store
|
||||||
const { captureEvent } = useEventTracker();
|
const { captureEvent } = useEventTracker();
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const { addCycleToFavorites, removeCycleFromFavorites, getCycleById } = useCycle();
|
const { addCycleToFavorites, removeCycleFromFavorites, getCycleById } = useCycle();
|
||||||
const { getUserDetails } = useMember();
|
const { getUserDetails } = useMember();
|
||||||
// computed
|
// computed
|
||||||
|
|
@ -57,7 +56,10 @@ export const CyclesBoardCard: FC<ICyclesBoardCard> = observer((props) => {
|
||||||
const startDate = getDate(cycleDetails.start_date);
|
const startDate = getDate(cycleDetails.start_date);
|
||||||
const isDateValid = cycleDetails.start_date || cycleDetails.end_date;
|
const isDateValid = cycleDetails.start_date || cycleDetails.end_date;
|
||||||
|
|
||||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
|
const isEditingAllowed = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.PROJECT
|
||||||
|
);
|
||||||
|
|
||||||
const currentCycle = CYCLE_STATUS.find((status) => status.value === cycleStatus);
|
const currentCycle = CYCLE_STATUS.find((status) => status.value === cycleStatus);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,14 @@ import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
|
||||||
// constants
|
// constants
|
||||||
import { CYCLE_STATUS } from "@/constants/cycle";
|
import { CYCLE_STATUS } from "@/constants/cycle";
|
||||||
import { CYCLE_FAVORITED, CYCLE_UNFAVORITED } from "@/constants/event-tracker";
|
import { CYCLE_FAVORITED, CYCLE_UNFAVORITED } from "@/constants/event-tracker";
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
|
||||||
// helpers
|
// helpers
|
||||||
import { findHowManyDaysLeft, getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
import { findHowManyDaysLeft, getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useCycle, useEventTracker, useMember, useUser } from "@/hooks/store";
|
import { useCycle, useEventTracker, useMember, useUserPermissions } from "@/hooks/store";
|
||||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
import { CycleService } from "@/services/cycle.service";
|
import { CycleService } from "@/services/cycle.service";
|
||||||
|
|
||||||
const cycleService = new CycleService();
|
const cycleService = new CycleService();
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
@ -44,9 +45,8 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
|
||||||
// store hooks
|
// store hooks
|
||||||
const { addCycleToFavorites, removeCycleFromFavorites, updateCycleDetails } = useCycle();
|
const { addCycleToFavorites, removeCycleFromFavorites, updateCycleDetails } = useCycle();
|
||||||
const { captureEvent } = useEventTracker();
|
const { captureEvent } = useEventTracker();
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const { getUserDetails } = useMember();
|
const { getUserDetails } = useMember();
|
||||||
|
|
||||||
// form
|
// form
|
||||||
|
|
@ -56,7 +56,10 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
|
||||||
|
|
||||||
// derived values
|
// derived values
|
||||||
const cycleStatus = cycleDetails.status ? (cycleDetails.status.toLocaleLowerCase() as TCycleGroups) : "draft";
|
const cycleStatus = cycleDetails.status ? (cycleDetails.status.toLocaleLowerCase() as TCycleGroups) : "draft";
|
||||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
const isEditingAllowed = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.PROJECT
|
||||||
|
);
|
||||||
const renderIcon = Boolean(cycleDetails.start_date) || Boolean(cycleDetails.end_date);
|
const renderIcon = Boolean(cycleDetails.start_date) || Boolean(cycleDetails.end_date);
|
||||||
const currentCycle = CYCLE_STATUS.find((status) => status.value === cycleStatus);
|
const currentCycle = CYCLE_STATUS.find((status) => status.value === cycleStatus);
|
||||||
const daysLeft = findHowManyDaysLeft(cycleDetails.end_date) ?? 0;
|
const daysLeft = findHowManyDaysLeft(cycleDetails.end_date) ?? 0;
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,13 @@ import { ArchiveRestoreIcon, ExternalLink, LinkIcon, Pencil, Trash2 } from "luci
|
||||||
import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, setToast } from "@plane/ui";
|
import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { ArchiveCycleModal, CycleCreateUpdateModal, CycleDeleteModal } from "@/components/cycles";
|
import { ArchiveCycleModal, CycleCreateUpdateModal, CycleDeleteModal } from "@/components/cycles";
|
||||||
// constants
|
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useCycle, useEventTracker, useUser } from "@/hooks/store";
|
import { useCycle, useEventTracker, useUserPermissions } from "@/hooks/store";
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
parentRef: React.RefObject<HTMLElement>;
|
parentRef: React.RefObject<HTMLElement>;
|
||||||
|
|
@ -35,17 +34,19 @@ export const CycleQuickActions: React.FC<Props> = observer((props) => {
|
||||||
const [deleteModal, setDeleteModal] = useState(false);
|
const [deleteModal, setDeleteModal] = useState(false);
|
||||||
// store hooks
|
// store hooks
|
||||||
const { setTrackElement } = useEventTracker();
|
const { setTrackElement } = useEventTracker();
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentWorkspaceAllProjectsRole },
|
|
||||||
} = useUser();
|
|
||||||
const { getCycleById, restoreCycle } = useCycle();
|
const { getCycleById, restoreCycle } = useCycle();
|
||||||
// derived values
|
// derived values
|
||||||
const cycleDetails = getCycleById(cycleId);
|
const cycleDetails = getCycleById(cycleId);
|
||||||
const isArchived = !!cycleDetails?.archived_at;
|
const isArchived = !!cycleDetails?.archived_at;
|
||||||
const isCompleted = cycleDetails?.status?.toLowerCase() === "completed";
|
const isCompleted = cycleDetails?.status?.toLowerCase() === "completed";
|
||||||
// auth
|
// auth
|
||||||
const isEditingAllowed =
|
const isEditingAllowed = allowPermissions(
|
||||||
!!currentWorkspaceAllProjectsRole && currentWorkspaceAllProjectsRole[projectId] >= EUserProjectRoles.MEMBER;
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.PROJECT,
|
||||||
|
workspaceSlug,
|
||||||
|
projectId
|
||||||
|
);
|
||||||
|
|
||||||
const cycleLink = `${workspaceSlug}/projects/${projectId}/cycles/${cycleId}`;
|
const cycleLink = `${workspaceSlug}/projects/${projectId}/cycles/${cycleId}`;
|
||||||
const handleCopyText = () =>
|
const handleCopyText = () =>
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,9 @@ import { observer } from "mobx-react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
// ui
|
// ui
|
||||||
import { Button } from "@plane/ui";
|
import { Button } from "@plane/ui";
|
||||||
// constants
|
|
||||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useCommandPalette, useEventTracker, useUser } from "@/hooks/store";
|
import { useCommandPalette, useEventTracker, useUserPermissions } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
// assets
|
// assets
|
||||||
import ProjectEmptyStateImage from "@/public/empty-state/onboarding/dashboard-light.webp";
|
import ProjectEmptyStateImage from "@/public/empty-state/onboarding/dashboard-light.webp";
|
||||||
|
|
||||||
|
|
@ -15,11 +14,10 @@ export const DashboardProjectEmptyState = observer(() => {
|
||||||
// store hooks
|
// store hooks
|
||||||
const { toggleCreateProjectModal } = useCommandPalette();
|
const { toggleCreateProjectModal } = useCommandPalette();
|
||||||
const { setTrackElement } = useEventTracker();
|
const { setTrackElement } = useEventTracker();
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentWorkspaceRole },
|
|
||||||
} = useUser();
|
|
||||||
// derived values
|
// derived values
|
||||||
const canCreateProject = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
const canCreateProject = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto flex h-full flex-col justify-center space-y-4 lg:w-3/5">
|
<div className="mx-auto flex h-full flex-col justify-center space-y-4 lg:w-3/5">
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,9 @@ import { Logo } from "@/components/common";
|
||||||
import { WidgetLoader, WidgetProps } from "@/components/dashboard/widgets";
|
import { WidgetLoader, WidgetProps } from "@/components/dashboard/widgets";
|
||||||
// constants
|
// constants
|
||||||
import { PROJECT_BACKGROUND_COLORS } from "@/constants/dashboard";
|
import { PROJECT_BACKGROUND_COLORS } from "@/constants/dashboard";
|
||||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useEventTracker, useDashboard, useProject, useUser, useCommandPalette } from "@/hooks/store";
|
import { useEventTracker, useDashboard, useProject, useCommandPalette, useUserPermissions } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
const WIDGET_KEY = "recent_projects";
|
const WIDGET_KEY = "recent_projects";
|
||||||
|
|
||||||
|
|
@ -65,13 +65,14 @@ export const RecentProjectsWidget: React.FC<WidgetProps> = observer((props) => {
|
||||||
// store hooks
|
// store hooks
|
||||||
const { toggleCreateProjectModal } = useCommandPalette();
|
const { toggleCreateProjectModal } = useCommandPalette();
|
||||||
const { setTrackElement } = useEventTracker();
|
const { setTrackElement } = useEventTracker();
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentWorkspaceRole },
|
|
||||||
} = useUser();
|
|
||||||
const { fetchWidgetStats, getWidgetStats } = useDashboard();
|
const { fetchWidgetStats, getWidgetStats } = useDashboard();
|
||||||
// derived values
|
// derived values
|
||||||
const widgetStats = getWidgetStats<TRecentProjectsWidgetResponse>(workspaceSlug, dashboardId, WIDGET_KEY);
|
const widgetStats = getWidgetStats<TRecentProjectsWidgetResponse>(workspaceSlug, dashboardId, WIDGET_KEY);
|
||||||
const canCreateProject = currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
const canCreateProject = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.WORKSPACE
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchWidgetStats(workspaceSlug, dashboardId, {
|
fetchWidgetStats(workspaceSlug, dashboardId, {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ import { Button, TButtonVariant } from "@plane/ui";
|
||||||
import { EMPTY_STATE_DETAILS, EmptyStateType } from "@/constants/empty-state";
|
import { EMPTY_STATE_DETAILS, EmptyStateType } from "@/constants/empty-state";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { useUser } from "@/hooks/store";
|
import { useUserPermissions } from "@/hooks/store";
|
||||||
|
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
import { ComicBoxButton } from "./comic-box-button";
|
import { ComicBoxButton } from "./comic-box-button";
|
||||||
|
|
||||||
export type EmptyStateProps = {
|
export type EmptyStateProps = {
|
||||||
|
|
@ -37,9 +38,7 @@ export const EmptyState: React.FC<EmptyStateProps> = observer((props) => {
|
||||||
secondaryButtonOnClick,
|
secondaryButtonOnClick,
|
||||||
} = props;
|
} = props;
|
||||||
// store
|
// store
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentWorkspaceRole, currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
// theme
|
// theme
|
||||||
const { resolvedTheme } = useTheme();
|
const { resolvedTheme } = useTheme();
|
||||||
|
|
||||||
|
|
@ -53,10 +52,14 @@ export const EmptyState: React.FC<EmptyStateProps> = observer((props) => {
|
||||||
const resolvedEmptyStatePath = `${additionalPath && additionalPath !== "" ? `${path}${additionalPath}` : path}-${
|
const resolvedEmptyStatePath = `${additionalPath && additionalPath !== "" ? `${path}${additionalPath}` : path}-${
|
||||||
resolvedTheme === "light" ? "light" : "dark"
|
resolvedTheme === "light" ? "light" : "dark"
|
||||||
}.webp`;
|
}.webp`;
|
||||||
// current access type
|
|
||||||
const currentAccessType = accessType === "workspace" ? currentWorkspaceRole : currentProjectRole;
|
|
||||||
// permission
|
// permission
|
||||||
const isEditingAllowed = currentAccessType && access && currentAccessType >= access;
|
const isEditingAllowed =
|
||||||
|
access &&
|
||||||
|
accessType &&
|
||||||
|
allowPermissions(
|
||||||
|
access,
|
||||||
|
accessType === "workspace" ? EUserPermissionsLevel.WORKSPACE : EUserPermissionsLevel.PROJECT
|
||||||
|
);
|
||||||
const anyButton = primaryButton || secondaryButton;
|
const anyButton = primaryButton || secondaryButton;
|
||||||
|
|
||||||
// primary button
|
// primary button
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,11 @@ import { ImportExportSettingsLoader } from "@/components/ui";
|
||||||
// constants
|
// constants
|
||||||
import { EmptyStateType } from "@/constants/empty-state";
|
import { EmptyStateType } from "@/constants/empty-state";
|
||||||
import { EXPORT_SERVICES_LIST } from "@/constants/fetch-keys";
|
import { EXPORT_SERVICES_LIST } from "@/constants/fetch-keys";
|
||||||
import { EUserWorkspaceRoles, EXPORTERS_LIST } from "@/constants/workspace";
|
import { EXPORTERS_LIST } from "@/constants/workspace";
|
||||||
// hooks
|
// hooks
|
||||||
import { useProject, useUser } from "@/hooks/store";
|
import { useProject, useUser, useUserPermissions } from "@/hooks/store";
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
// services
|
// services
|
||||||
import { IntegrationService } from "@/services/integrations";
|
import { IntegrationService } from "@/services/integrations";
|
||||||
|
|
||||||
|
|
@ -37,11 +38,9 @@ const IntegrationGuide = observer(() => {
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const provider = searchParams.get("provider");
|
const provider = searchParams.get("provider");
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const { data: currentUser, canPerformAnyCreateAction } = useUser();
|
||||||
data: currentUser,
|
const { allowPermissions } = useUserPermissions();
|
||||||
canPerformAnyCreateAction,
|
|
||||||
membership: { currentWorkspaceRole },
|
|
||||||
} = useUser();
|
|
||||||
const { workspaceProjectIds } = useProject();
|
const { workspaceProjectIds } = useProject();
|
||||||
|
|
||||||
const { data: exporterServices } = useSWR(
|
const { data: exporterServices } = useSWR(
|
||||||
|
|
@ -61,7 +60,7 @@ const IntegrationGuide = observer(() => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasProjects = workspaceProjectIds && workspaceProjectIds.length > 0;
|
const hasProjects = workspaceProjectIds && workspaceProjectIds.length > 0;
|
||||||
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
|
|
|
||||||
|
|
@ -27,15 +27,14 @@ import {
|
||||||
SelectDuplicateInboxIssueModal,
|
SelectDuplicateInboxIssueModal,
|
||||||
} from "@/components/inbox";
|
} from "@/components/inbox";
|
||||||
import { IssueUpdateStatus } from "@/components/issues";
|
import { IssueUpdateStatus } from "@/components/issues";
|
||||||
// constants
|
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
|
||||||
// helpers
|
// helpers
|
||||||
import { findHowManyDaysLeft } from "@/helpers/date-time.helper";
|
import { findHowManyDaysLeft } from "@/helpers/date-time.helper";
|
||||||
import { EInboxIssueStatus } from "@/helpers/inbox.helper";
|
import { EInboxIssueStatus } from "@/helpers/inbox.helper";
|
||||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser, useProjectInbox, useProject } from "@/hooks/store";
|
import { useUser, useProjectInbox, useProject, useUserPermissions } from "@/hooks/store";
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
// store types
|
// store types
|
||||||
import type { IInboxIssueStore } from "@/store/inbox/inbox-issue.store";
|
import type { IInboxIssueStore } from "@/store/inbox/inbox-issue.store";
|
||||||
|
|
||||||
|
|
@ -70,23 +69,26 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
|
||||||
// store
|
// store
|
||||||
const { currentTab, deleteInboxIssue, filteredInboxIssueIds } = useProjectInbox();
|
const { currentTab, deleteInboxIssue, filteredInboxIssueIds } = useProjectInbox();
|
||||||
const { data: currentUser } = useUser();
|
const { data: currentUser } = useUser();
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentProjectRoleByProjectId },
|
|
||||||
} = useUser();
|
|
||||||
|
|
||||||
const router = useAppRouter();
|
const router = useAppRouter();
|
||||||
const { getProjectById } = useProject();
|
const { getProjectById } = useProject();
|
||||||
|
|
||||||
const issue = inboxIssue?.issue;
|
const issue = inboxIssue?.issue;
|
||||||
// derived values
|
// derived values
|
||||||
const currentProjectRole = currentProjectRoleByProjectId(projectId) || undefined;
|
const isAllowed = allowPermissions(
|
||||||
const isAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.PROJECT,
|
||||||
|
workspaceSlug,
|
||||||
|
projectId
|
||||||
|
);
|
||||||
const canMarkAsDuplicate = isAllowed && (inboxIssue?.status === 0 || inboxIssue?.status === -2);
|
const canMarkAsDuplicate = isAllowed && (inboxIssue?.status === 0 || inboxIssue?.status === -2);
|
||||||
const canMarkAsAccepted = isAllowed && (inboxIssue?.status === 0 || inboxIssue?.status === -2);
|
const canMarkAsAccepted = isAllowed && (inboxIssue?.status === 0 || inboxIssue?.status === -2);
|
||||||
const canMarkAsDeclined = isAllowed && (inboxIssue?.status === 0 || inboxIssue?.status === -2);
|
const canMarkAsDeclined = isAllowed && (inboxIssue?.status === 0 || inboxIssue?.status === -2);
|
||||||
// can delete only if admin or is creator of the issue
|
// can delete only if admin or is creator of the issue
|
||||||
const canDelete =
|
const canDelete =
|
||||||
(!!currentProjectRole && currentProjectRole >= EUserProjectRoles.ADMIN) || issue?.created_by === currentUser?.id;
|
allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT, workspaceSlug, projectId) ||
|
||||||
|
issue?.created_by === currentUser?.id;
|
||||||
const isAcceptedOrDeclined = inboxIssue?.status ? [-1, 1, 2].includes(inboxIssue.status) : undefined;
|
const isAcceptedOrDeclined = inboxIssue?.status ? [-1, 1, 2].includes(inboxIssue.status) : undefined;
|
||||||
// days left for snooze
|
// days left for snooze
|
||||||
const numberOfDaysLeft = findHowManyDaysLeft(inboxIssue?.snoozed_till);
|
const numberOfDaysLeft = findHowManyDaysLeft(inboxIssue?.snoozed_till);
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,10 @@ import useSWR from "swr";
|
||||||
// components
|
// components
|
||||||
import { ContentWrapper } from "@plane/ui";
|
import { ContentWrapper } from "@plane/ui";
|
||||||
import { InboxIssueActionsHeader, InboxIssueMainContent } from "@/components/inbox";
|
import { InboxIssueActionsHeader, InboxIssueMainContent } from "@/components/inbox";
|
||||||
// constants
|
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useProjectInbox, useUser } from "@/hooks/store";
|
import { useProjectInbox, useUserPermissions } from "@/hooks/store";
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
type TInboxContentRoot = {
|
type TInboxContentRoot = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
|
|
@ -37,9 +36,8 @@ export const InboxContentRoot: FC<TInboxContentRoot> = observer((props) => {
|
||||||
// hooks
|
// hooks
|
||||||
const { currentTab, fetchInboxIssueById, getIssueInboxByIssueId, getIsIssueAvailable } = useProjectInbox();
|
const { currentTab, fetchInboxIssueById, getIssueInboxByIssueId, getIsIssueAvailable } = useProjectInbox();
|
||||||
const inboxIssue = getIssueInboxByIssueId(inboxIssueId);
|
const inboxIssue = getIssueInboxByIssueId(inboxIssueId);
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
// derived values
|
// derived values
|
||||||
const isIssueAvailable = getIsIssueAvailable(inboxIssueId?.toString() || "");
|
const isIssueAvailable = getIsIssueAvailable(inboxIssueId?.toString() || "");
|
||||||
|
|
||||||
|
|
@ -63,7 +61,7 @@ export const InboxContentRoot: FC<TInboxContentRoot> = observer((props) => {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const isEditable = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
const isEditable = allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT);
|
||||||
|
|
||||||
if (!inboxIssue) return <></>;
|
if (!inboxIssue) return <></>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,10 @@ import { Button, Loader, Tooltip, TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
// constants
|
// constants
|
||||||
import { WORKSPACE_INTEGRATIONS } from "@/constants/fetch-keys";
|
import { WORKSPACE_INTEGRATIONS } from "@/constants/fetch-keys";
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser, useInstance } from "@/hooks/store";
|
import { useInstance, useUserPermissions } from "@/hooks/store";
|
||||||
import useIntegrationPopup from "@/hooks/use-integration-popup";
|
import useIntegrationPopup from "@/hooks/use-integration-popup";
|
||||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
// services
|
// services
|
||||||
// icons
|
// icons
|
||||||
import GithubLogo from "@/public/services/github.png";
|
import GithubLogo from "@/public/services/github.png";
|
||||||
|
|
@ -48,11 +49,9 @@ export const SingleIntegrationCard: React.FC<Props> = observer(({ integration })
|
||||||
const { workspaceSlug } = useParams();
|
const { workspaceSlug } = useParams();
|
||||||
// store hooks
|
// store hooks
|
||||||
const { config } = useInstance();
|
const { config } = useInstance();
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentWorkspaceRole },
|
|
||||||
} = useUser();
|
|
||||||
|
|
||||||
const isUserAdmin = currentWorkspaceRole === 20;
|
const isUserAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||||
const { isMobile } = usePlatformOS();
|
const { isMobile } = usePlatformOS();
|
||||||
const { startAuth, isConnecting: isInstalling } = useIntegrationPopup({
|
const { startAuth, isConnecting: isInstalling } = useIntegrationPopup({
|
||||||
provider: integration.provider,
|
provider: integration.provider,
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ import { AlertModalCore, TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
// constants
|
// constants
|
||||||
import { PROJECT_ERROR_MESSAGES } from "@/constants/project";
|
import { PROJECT_ERROR_MESSAGES } from "@/constants/project";
|
||||||
// hooks
|
// hooks
|
||||||
import { useIssues, useProject, useUser } from "@/hooks/store";
|
import { useIssues, useProject, useUser, useUserPermissions } from "@/hooks/store";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
type Props = {
|
type Props = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
handleClose: () => void;
|
handleClose: () => void;
|
||||||
|
|
@ -26,7 +26,12 @@ export const DeleteIssueModal: React.FC<Props> = (props) => {
|
||||||
// store hooks
|
// store hooks
|
||||||
const { issueMap } = useIssues();
|
const { issueMap } = useIssues();
|
||||||
const { getProjectById } = useProject();
|
const { getProjectById } = useProject();
|
||||||
const { data: currentUser, canPerformProjectAdminActions } = useUser();
|
const { allowPermissions } = useUserPermissions();
|
||||||
|
|
||||||
|
const { data: currentUser } = useUser();
|
||||||
|
|
||||||
|
// derived values
|
||||||
|
const canPerformProjectAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsDeleting(false);
|
setIsDeleting(false);
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,22 @@ import { ArchiveIssueModal, DeleteIssueModal, IssueSubscription } from "@/compon
|
||||||
// constants
|
// constants
|
||||||
import { ISSUE_ARCHIVED, ISSUE_DELETED } from "@/constants/event-tracker";
|
import { ISSUE_ARCHIVED, ISSUE_DELETED } from "@/constants/event-tracker";
|
||||||
import { EIssuesStoreType } from "@/constants/issue";
|
import { EIssuesStoreType } from "@/constants/issue";
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
|
||||||
import { ARCHIVABLE_STATE_GROUPS } from "@/constants/state";
|
import { ARCHIVABLE_STATE_GROUPS } from "@/constants/state";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { copyTextToClipboard } from "@/helpers/string.helper";
|
import { copyTextToClipboard } from "@/helpers/string.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useEventTracker, useIssueDetail, useIssues, useProjectState, useUser } from "@/hooks/store";
|
import {
|
||||||
|
useEventTracker,
|
||||||
|
useIssueDetail,
|
||||||
|
useIssues,
|
||||||
|
useProjectState,
|
||||||
|
useUser,
|
||||||
|
useUserPermissions,
|
||||||
|
} from "@/hooks/store";
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
|
|
@ -38,10 +45,8 @@ export const IssueDetailQuickActions: FC<Props> = observer((props) => {
|
||||||
const router = useAppRouter();
|
const router = useAppRouter();
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
const {
|
const { data: currentUser } = useUser();
|
||||||
data: currentUser,
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const { isMobile } = usePlatformOS();
|
const { isMobile } = usePlatformOS();
|
||||||
const { getStateById } = useProjectState();
|
const { getStateById } = useProjectState();
|
||||||
const {
|
const {
|
||||||
|
|
@ -149,8 +154,11 @@ export const IssueDetailQuickActions: FC<Props> = observer((props) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// auth
|
// auth
|
||||||
const isEditable = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
const isEditable = allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT);
|
||||||
const canRestoreIssue = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
const canRestoreIssue = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.PROJECT
|
||||||
|
);
|
||||||
const isArchivingAllowed = !issue?.archived_at && isEditable;
|
const isArchivingAllowed = !issue?.archived_at && isEditable;
|
||||||
const isInArchivableGroup = !!stateDetails && ARCHIVABLE_STATE_GROUPS.includes(stateDetails?.group);
|
const isInArchivableGroup = !!stateDetails && ARCHIVABLE_STATE_GROUPS.includes(stateDetails?.group);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,10 @@ import { IssuePeekOverview } from "@/components/issues";
|
||||||
// constants
|
// constants
|
||||||
import { ISSUE_UPDATED, ISSUE_DELETED, ISSUE_ARCHIVED } from "@/constants/event-tracker";
|
import { ISSUE_UPDATED, ISSUE_DELETED, ISSUE_ARCHIVED } from "@/constants/event-tracker";
|
||||||
import { EIssuesStoreType } from "@/constants/issue";
|
import { EIssuesStoreType } from "@/constants/issue";
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useAppTheme, useEventTracker, useIssueDetail, useIssues, useUser } from "@/hooks/store";
|
import { useAppTheme, useEventTracker, useIssueDetail, useIssues, useUserPermissions } from "@/hooks/store";
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
// images
|
// images
|
||||||
import emptyIssue from "@/public/empty-state/issue.svg";
|
import emptyIssue from "@/public/empty-state/issue.svg";
|
||||||
// local components
|
// local components
|
||||||
|
|
@ -77,9 +77,7 @@ export const IssueDetailRoot: FC<TIssueDetailRoot> = observer((props) => {
|
||||||
issues: { removeIssue: removeArchivedIssue },
|
issues: { removeIssue: removeArchivedIssue },
|
||||||
} = useIssues(EIssuesStoreType.ARCHIVED);
|
} = useIssues(EIssuesStoreType.ARCHIVED);
|
||||||
const { captureIssueEvent } = useEventTracker();
|
const { captureIssueEvent } = useEventTracker();
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const { issueDetailSidebarCollapsed } = useAppTheme();
|
const { issueDetailSidebarCollapsed } = useAppTheme();
|
||||||
|
|
||||||
const issueOperations: TIssueOperations = useMemo(
|
const issueOperations: TIssueOperations = useMemo(
|
||||||
|
|
@ -332,7 +330,7 @@ export const IssueDetailRoot: FC<TIssueDetailRoot> = observer((props) => {
|
||||||
// issue details
|
// issue details
|
||||||
const issue = getIssueById(issueId);
|
const issue = getIssueById(issueId);
|
||||||
// checking if issue is editable, based on user role
|
// checking if issue is editable, based on user role
|
||||||
const isEditable = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
const isEditable = allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,11 @@ import { TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
import { CalendarChart } from "@/components/issues";
|
import { CalendarChart } from "@/components/issues";
|
||||||
//constants
|
//constants
|
||||||
import { EIssuesStoreType } from "@/constants/issue";
|
import { EIssuesStoreType } from "@/constants/issue";
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useIssues, useUser, useCalendarView } from "@/hooks/store";
|
import { useIssues, useCalendarView, useUserPermissions } from "@/hooks/store";
|
||||||
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
|
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
|
||||||
import { useIssuesActions } from "@/hooks/use-issues-actions";
|
import { useIssuesActions } from "@/hooks/use-issues-actions";
|
||||||
|
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||||
// types
|
// types
|
||||||
import { IQuickActionProps } from "../list/list-view-types";
|
import { IQuickActionProps } from "../list/list-view-types";
|
||||||
import { handleDragDrop } from "./utils";
|
import { handleDragDrop } from "./utils";
|
||||||
|
|
@ -40,9 +40,7 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
const storeType = useIssueStoreType() as CalendarStoreType;
|
const storeType = useIssueStoreType() as CalendarStoreType;
|
||||||
const {
|
const { allowPermissions } = useUserPermissions();
|
||||||
membership: { currentProjectRole },
|
|
||||||
} = useUser();
|
|
||||||
const { issues, issuesFilter, issueMap } = useIssues(storeType);
|
const { issues, issuesFilter, issueMap } = useIssues(storeType);
|
||||||
const {
|
const {
|
||||||
fetchIssues,
|
fetchIssues,
|
||||||
|
|
@ -58,7 +56,10 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
|
||||||
|
|
||||||
const issueCalendarView = useCalendarView();
|
const issueCalendarView = useCalendarView();
|
||||||
|
|
||||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
const isEditingAllowed = allowPermissions(
|
||||||
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
EUserPermissionsLevel.PROJECT
|
||||||
|
);
|
||||||
|
|
||||||
const displayFilters = issuesFilter.issueFilters?.displayFilters;
|
const displayFilters = issuesFilter.issueFilters?.displayFilters;
|
||||||
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue