ФУНКЦИИ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: очистка stale assignees Operational Core
This commit is contained in:
parent
fa4a533d29
commit
78deab1a23
|
|
@ -14,7 +14,7 @@ from drf_spectacular.utils import (
|
||||||
# Module imports
|
# Module imports
|
||||||
from .base import BaseAPIView
|
from .base import BaseAPIView
|
||||||
from plane.api.serializers import UserLiteSerializer, ProjectMemberSerializer
|
from plane.api.serializers import UserLiteSerializer, ProjectMemberSerializer
|
||||||
from plane.db.models import User, Workspace, WorkspaceMember, ProjectMember
|
from plane.db.models import IssueAssignee, User, Workspace, WorkspaceMember, ProjectMember
|
||||||
from plane.utils.permissions import ProjectMemberPermission, WorkSpaceAdminPermission, ProjectAdminPermission
|
from plane.utils.permissions import ProjectMemberPermission, WorkSpaceAdminPermission, ProjectAdminPermission
|
||||||
from plane.utils.openapi import (
|
from plane.utils.openapi import (
|
||||||
WORKSPACE_SLUG_PARAMETER,
|
WORKSPACE_SLUG_PARAMETER,
|
||||||
|
|
@ -205,6 +205,8 @@ class ProjectMemberDetailAPIEndpoint(ProjectMemberListCreateAPIEndpoint):
|
||||||
serializer = ProjectMemberSerializer(project_member, data=request.data, partial=True, context={"slug": slug})
|
serializer = ProjectMemberSerializer(project_member, data=request.data, partial=True, context={"slug": slug})
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
serializer.save()
|
serializer.save()
|
||||||
|
if serializer.instance.is_active is False:
|
||||||
|
IssueAssignee.objects.filter(project_id=project_id, assignee_id=serializer.instance.member_id).delete()
|
||||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
@extend_schema(
|
@extend_schema(
|
||||||
|
|
@ -219,4 +221,5 @@ class ProjectMemberDetailAPIEndpoint(ProjectMemberListCreateAPIEndpoint):
|
||||||
project_member = ProjectMember.objects.get(project_id=project_id, workspace__slug=slug, pk=pk)
|
project_member = ProjectMember.objects.get(project_id=project_id, workspace__slug=slug, pk=pk)
|
||||||
project_member.is_active = False
|
project_member.is_active = False
|
||||||
project_member.save()
|
project_member.save()
|
||||||
|
IssueAssignee.objects.filter(project_id=project_id, assignee_id=project_member.member_id).delete()
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
|
||||||
|
|
@ -570,7 +570,15 @@ class IssueViewSet(BaseViewSet):
|
||||||
Subquery(
|
Subquery(
|
||||||
IssueAssignee.objects.filter(
|
IssueAssignee.objects.filter(
|
||||||
issue_id=OuterRef("pk"),
|
issue_id=OuterRef("pk"),
|
||||||
|
deleted_at__isnull=True,
|
||||||
|
assignee__is_active=True,
|
||||||
|
assignee__member_workspace__workspace_id=OuterRef("workspace_id"),
|
||||||
|
assignee__member_workspace__is_active=True,
|
||||||
|
assignee__member_workspace__is_banned=False,
|
||||||
|
assignee__member_workspace__deleted_at__isnull=True,
|
||||||
|
assignee__member_project__project_id=OuterRef("project_id"),
|
||||||
assignee__member_project__is_active=True,
|
assignee__member_project__is_active=True,
|
||||||
|
assignee__member_project__deleted_at__isnull=True,
|
||||||
)
|
)
|
||||||
.values("issue_id")
|
.values("issue_id")
|
||||||
.annotate(arr=ArrayAgg("assignee_id", distinct=True))
|
.annotate(arr=ArrayAgg("assignee_id", distinct=True))
|
||||||
|
|
@ -676,7 +684,14 @@ class IssueViewSet(BaseViewSet):
|
||||||
distinct=True,
|
distinct=True,
|
||||||
filter=Q(
|
filter=Q(
|
||||||
~Q(assignees__id__isnull=True)
|
~Q(assignees__id__isnull=True)
|
||||||
|
& Q(assignees__is_active=True)
|
||||||
|
& Q(assignees__member_workspace__workspace_id=F("workspace_id"))
|
||||||
|
& Q(assignees__member_workspace__is_active=True)
|
||||||
|
& Q(assignees__member_workspace__is_banned=False)
|
||||||
|
& Q(assignees__member_workspace__deleted_at__isnull=True)
|
||||||
|
& Q(assignees__member_project__project_id=F("project_id"))
|
||||||
& Q(assignees__member_project__is_active=True)
|
& Q(assignees__member_project__is_active=True)
|
||||||
|
& Q(assignees__member_project__deleted_at__isnull=True)
|
||||||
& Q(issue_assignee__deleted_at__isnull=True)
|
& Q(issue_assignee__deleted_at__isnull=True)
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -980,7 +995,15 @@ class IssuePaginatedViewSet(BaseViewSet):
|
||||||
Subquery(
|
Subquery(
|
||||||
IssueAssignee.objects.filter(
|
IssueAssignee.objects.filter(
|
||||||
issue_id=OuterRef("pk"),
|
issue_id=OuterRef("pk"),
|
||||||
|
deleted_at__isnull=True,
|
||||||
|
assignee__is_active=True,
|
||||||
|
assignee__member_workspace__workspace_id=OuterRef("workspace_id"),
|
||||||
|
assignee__member_workspace__is_active=True,
|
||||||
|
assignee__member_workspace__is_banned=False,
|
||||||
|
assignee__member_workspace__deleted_at__isnull=True,
|
||||||
|
assignee__member_project__project_id=OuterRef("project_id"),
|
||||||
assignee__member_project__is_active=True,
|
assignee__member_project__is_active=True,
|
||||||
|
assignee__member_project__deleted_at__isnull=True,
|
||||||
)
|
)
|
||||||
.values("issue_id")
|
.values("issue_id")
|
||||||
.annotate(arr=ArrayAgg("assignee_id", distinct=True))
|
.annotate(arr=ArrayAgg("assignee_id", distinct=True))
|
||||||
|
|
@ -1315,7 +1338,14 @@ class IssueDetailIdentifierEndpoint(BaseAPIView):
|
||||||
distinct=True,
|
distinct=True,
|
||||||
filter=Q(
|
filter=Q(
|
||||||
~Q(assignees__id__isnull=True)
|
~Q(assignees__id__isnull=True)
|
||||||
|
& Q(assignees__is_active=True)
|
||||||
|
& Q(assignees__member_workspace__workspace_id=F("workspace_id"))
|
||||||
|
& Q(assignees__member_workspace__is_active=True)
|
||||||
|
& Q(assignees__member_workspace__is_banned=False)
|
||||||
|
& Q(assignees__member_workspace__deleted_at__isnull=True)
|
||||||
|
& Q(assignees__member_project__project_id=F("project_id"))
|
||||||
& Q(assignees__member_project__is_active=True)
|
& Q(assignees__member_project__is_active=True)
|
||||||
|
& Q(assignees__member_project__deleted_at__isnull=True)
|
||||||
& Q(issue_assignee__deleted_at__isnull=True)
|
& Q(issue_assignee__deleted_at__isnull=True)
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ from plane.app.serializers import (
|
||||||
|
|
||||||
from plane.app.permissions import WorkspaceUserPermission
|
from plane.app.permissions import WorkspaceUserPermission
|
||||||
|
|
||||||
from plane.db.models import Project, ProjectMember, ProjectUserProperty, WorkspaceMember
|
from plane.db.models import IssueAssignee, Project, ProjectMember, ProjectUserProperty, WorkspaceMember
|
||||||
from plane.bgtasks.project_add_user_email_task import project_add_user_email
|
from plane.bgtasks.project_add_user_email_task import project_add_user_email
|
||||||
from plane.utils.host import base_host
|
from plane.utils.host import base_host
|
||||||
from plane.app.permissions.base import allow_permission, ROLE
|
from plane.app.permissions.base import allow_permission, ROLE
|
||||||
|
|
@ -295,6 +295,7 @@ class ProjectMemberViewSet(BaseViewSet):
|
||||||
|
|
||||||
project_member.is_active = False
|
project_member.is_active = False
|
||||||
project_member.save()
|
project_member.save()
|
||||||
|
IssueAssignee.objects.filter(project_id=project_id, assignee_id=project_member.member_id).delete()
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
|
|
@ -323,6 +324,7 @@ class ProjectMemberViewSet(BaseViewSet):
|
||||||
# Deactivate the user
|
# Deactivate the user
|
||||||
project_member.is_active = False
|
project_member.is_active = False
|
||||||
project_member.save()
|
project_member.save()
|
||||||
|
IssueAssignee.objects.filter(project_id=project_id, assignee_id=project_member.member_id).delete()
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ from plane.app.serializers import (
|
||||||
WorkSpaceMemberSerializer,
|
WorkSpaceMemberSerializer,
|
||||||
)
|
)
|
||||||
from plane.app.views.base import BaseAPIView
|
from plane.app.views.base import BaseAPIView
|
||||||
from plane.db.models import Project, ProjectMember, WorkspaceMember, DraftIssue
|
from plane.db.models import IssueAssignee, Project, ProjectMember, WorkspaceMember, DraftIssue
|
||||||
from plane.utils.cache import invalidate_cache
|
from plane.utils.cache import invalidate_cache
|
||||||
|
|
||||||
from .. import BaseViewSet
|
from .. import BaseViewSet
|
||||||
|
|
@ -144,6 +144,7 @@ class WorkSpaceMemberViewSet(BaseViewSet):
|
||||||
_ = ProjectMember.objects.filter(
|
_ = ProjectMember.objects.filter(
|
||||||
workspace__slug=slug, member_id=workspace_member.member_id, is_active=True
|
workspace__slug=slug, member_id=workspace_member.member_id, is_active=True
|
||||||
).update(is_active=False, updated_at=timezone.now())
|
).update(is_active=False, updated_at=timezone.now())
|
||||||
|
IssueAssignee.objects.filter(workspace__slug=slug, assignee_id=workspace_member.member_id).delete()
|
||||||
|
|
||||||
workspace_member.is_active = False
|
workspace_member.is_active = False
|
||||||
workspace_member.save()
|
workspace_member.save()
|
||||||
|
|
@ -198,6 +199,7 @@ class WorkSpaceMemberViewSet(BaseViewSet):
|
||||||
_ = ProjectMember.objects.filter(
|
_ = ProjectMember.objects.filter(
|
||||||
workspace__slug=slug, member_id=workspace_member.member_id, is_active=True
|
workspace__slug=slug, member_id=workspace_member.member_id, is_active=True
|
||||||
).update(is_active=False, updated_at=timezone.now())
|
).update(is_active=False, updated_at=timezone.now())
|
||||||
|
IssueAssignee.objects.filter(workspace__slug=slug, assignee_id=workspace_member.member_id).delete()
|
||||||
|
|
||||||
# # Deactivate the user
|
# # Deactivate the user
|
||||||
workspace_member.is_active = False
|
workspace_member.is_active = False
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,16 @@ from django.views import View
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
from plane.authentication.views.nodedc_logout import is_internal_logout_request_authorized
|
from plane.authentication.views.nodedc_logout import is_internal_logout_request_authorized
|
||||||
from plane.db.models import ExternalIdentityLink, Profile, Project, ProjectMember, User, Workspace, WorkspaceMember
|
from plane.db.models import (
|
||||||
|
ExternalIdentityLink,
|
||||||
|
IssueAssignee,
|
||||||
|
Profile,
|
||||||
|
Project,
|
||||||
|
ProjectMember,
|
||||||
|
User,
|
||||||
|
Workspace,
|
||||||
|
WorkspaceMember,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
OIDC_PROVIDER = "authentik"
|
OIDC_PROVIDER = "authentik"
|
||||||
|
|
@ -384,6 +393,10 @@ class NodeDCInternalWorkspaceMembershipRemoveEndpoint(View):
|
||||||
member=user,
|
member=user,
|
||||||
is_active=True,
|
is_active=True,
|
||||||
).update(is_active=False)
|
).update(is_active=False)
|
||||||
|
IssueAssignee.objects.filter(
|
||||||
|
workspace=workspace,
|
||||||
|
assignee=user,
|
||||||
|
).delete()
|
||||||
membership.is_active = False
|
membership.is_active = False
|
||||||
membership.save(update_fields=["is_active", "updated_at"])
|
membership.save(update_fields=["is_active", "updated_at"])
|
||||||
|
|
||||||
|
|
@ -534,6 +547,7 @@ class NodeDCInternalProjectMembershipRemoveEndpoint(View):
|
||||||
|
|
||||||
project_member.is_active = False
|
project_member.is_active = False
|
||||||
project_member.save(update_fields=["is_active", "updated_at"])
|
project_member.save(update_fields=["is_active", "updated_at"])
|
||||||
|
IssueAssignee.objects.filter(project=project, assignee=user).delete()
|
||||||
|
|
||||||
return JsonResponse(
|
return JsonResponse(
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ from django.utils.dateparse import parse_datetime
|
||||||
from plane.app.views.base import BaseAPIView
|
from plane.app.views.base import BaseAPIView
|
||||||
from plane.license.api.permissions import InstanceAdminPermission
|
from plane.license.api.permissions import InstanceAdminPermission
|
||||||
from plane.db.models import (
|
from plane.db.models import (
|
||||||
|
IssueAssignee,
|
||||||
Project,
|
Project,
|
||||||
ProjectMember,
|
ProjectMember,
|
||||||
Workspace,
|
Workspace,
|
||||||
|
|
@ -278,6 +279,7 @@ class InstanceWorkSpaceMemberEndpoint(BaseAPIView):
|
||||||
member_id=workspace_member.member_id,
|
member_id=workspace_member.member_id,
|
||||||
is_active=True,
|
is_active=True,
|
||||||
).update(is_active=False, updated_at=timezone.now())
|
).update(is_active=False, updated_at=timezone.now())
|
||||||
|
IssueAssignee.objects.filter(workspace_id=workspace_id, assignee_id=workspace_member.member_id).delete()
|
||||||
|
|
||||||
workspace_member.is_active = False
|
workspace_member.is_active = False
|
||||||
workspace_member.save()
|
workspace_member.save()
|
||||||
|
|
@ -332,6 +334,7 @@ class InstanceWorkSpaceMemberBanEndpoint(BaseAPIView):
|
||||||
member_id=workspace_member.member_id,
|
member_id=workspace_member.member_id,
|
||||||
is_active=True,
|
is_active=True,
|
||||||
).update(is_active=False, updated_at=timezone.now())
|
).update(is_active=False, updated_at=timezone.now())
|
||||||
|
IssueAssignee.objects.filter(workspace_id=workspace_id, assignee_id=workspace_member.member_id).delete()
|
||||||
|
|
||||||
workspace_member.is_banned = True
|
workspace_member.is_banned = True
|
||||||
workspace_member.banned_at = timezone.now()
|
workspace_member.banned_at = timezone.now()
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,20 @@ def issue_queryset_grouper(
|
||||||
"module_ids": "issue_module__module_id",
|
"module_ids": "issue_module__module_id",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
valid_assignee_filter = (
|
||||||
|
Q(issue_assignee__deleted_at__isnull=True)
|
||||||
|
& Q(assignees__is_active=True)
|
||||||
|
& Q(assignees__member_workspace__workspace_id=F("workspace_id"))
|
||||||
|
& Q(assignees__member_workspace__is_active=True)
|
||||||
|
& Q(assignees__member_workspace__is_banned=False)
|
||||||
|
& Q(assignees__member_workspace__deleted_at__isnull=True)
|
||||||
|
& Q(assignees__member_project__project_id=F("project_id"))
|
||||||
|
& Q(assignees__member_project__is_active=True)
|
||||||
|
& Q(assignees__member_project__deleted_at__isnull=True)
|
||||||
|
)
|
||||||
|
|
||||||
GROUP_FILTER_MAPPER = {
|
GROUP_FILTER_MAPPER = {
|
||||||
"assignees__id": Q(issue_assignee__deleted_at__isnull=True),
|
"assignees__id": valid_assignee_filter,
|
||||||
"labels__id": Q(label_issue__deleted_at__isnull=True),
|
"labels__id": Q(label_issue__deleted_at__isnull=True),
|
||||||
"issue_module__module_id": Q(issue_module__deleted_at__isnull=True),
|
"issue_module__module_id": Q(issue_module__deleted_at__isnull=True),
|
||||||
}
|
}
|
||||||
|
|
@ -46,7 +58,7 @@ def issue_queryset_grouper(
|
||||||
annotations_map = {
|
annotations_map = {
|
||||||
"assignee_ids": (
|
"assignee_ids": (
|
||||||
"assignees__id",
|
"assignees__id",
|
||||||
~Q(assignees__id__isnull=True) & Q(issue_assignee__deleted_at__isnull=True),
|
~Q(assignees__id__isnull=True) & valid_assignee_filter,
|
||||||
),
|
),
|
||||||
"label_ids": (
|
"label_ids": (
|
||||||
"labels__id",
|
"labels__id",
|
||||||
|
|
|
||||||
|
|
@ -625,7 +625,14 @@ class IssueRetrievePublicEndpoint(BaseAPIView):
|
||||||
distinct=True,
|
distinct=True,
|
||||||
filter=Q(
|
filter=Q(
|
||||||
~Q(assignees__id__isnull=True)
|
~Q(assignees__id__isnull=True)
|
||||||
|
& Q(assignees__is_active=True)
|
||||||
|
& Q(assignees__member_workspace__workspace_id=F("workspace_id"))
|
||||||
|
& Q(assignees__member_workspace__is_active=True)
|
||||||
|
& Q(assignees__member_workspace__is_banned=False)
|
||||||
|
& Q(assignees__member_workspace__deleted_at__isnull=True)
|
||||||
|
& Q(assignees__member_project__project_id=F("project_id"))
|
||||||
& Q(assignees__member_project__is_active=True)
|
& Q(assignees__member_project__is_active=True)
|
||||||
|
& Q(assignees__member_project__deleted_at__isnull=True)
|
||||||
& Q(issue_assignee__deleted_at__isnull=True)
|
& Q(issue_assignee__deleted_at__isnull=True)
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
# Django imports
|
# Django imports
|
||||||
from django.contrib.postgres.aggregates import ArrayAgg
|
from django.contrib.postgres.aggregates import ArrayAgg
|
||||||
from django.contrib.postgres.fields import ArrayField
|
from django.contrib.postgres.fields import ArrayField
|
||||||
from django.db.models import Q, UUIDField, Value, QuerySet, OuterRef, Subquery
|
from django.db.models import F, Q, UUIDField, Value, QuerySet, OuterRef, Subquery
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
|
|
@ -37,8 +37,20 @@ def issue_queryset_grouper(
|
||||||
"module_ids": "issue_module__module_id",
|
"module_ids": "issue_module__module_id",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
valid_assignee_filter = (
|
||||||
|
Q(issue_assignee__deleted_at__isnull=True)
|
||||||
|
& Q(assignees__is_active=True)
|
||||||
|
& Q(assignees__member_workspace__workspace_id=F("workspace_id"))
|
||||||
|
& Q(assignees__member_workspace__is_active=True)
|
||||||
|
& Q(assignees__member_workspace__is_banned=False)
|
||||||
|
& Q(assignees__member_workspace__deleted_at__isnull=True)
|
||||||
|
& Q(assignees__member_project__project_id=F("project_id"))
|
||||||
|
& Q(assignees__member_project__is_active=True)
|
||||||
|
& Q(assignees__member_project__deleted_at__isnull=True)
|
||||||
|
)
|
||||||
|
|
||||||
GROUP_FILTER_MAPPER: Dict[str, Q] = {
|
GROUP_FILTER_MAPPER: Dict[str, Q] = {
|
||||||
"assignees__id": Q(issue_assignee__deleted_at__isnull=True),
|
"assignees__id": valid_assignee_filter,
|
||||||
"labels__id": Q(label_issue__deleted_at__isnull=True),
|
"labels__id": Q(label_issue__deleted_at__isnull=True),
|
||||||
"issue_module__module_id": Q(issue_module__deleted_at__isnull=True),
|
"issue_module__module_id": Q(issue_module__deleted_at__isnull=True),
|
||||||
}
|
}
|
||||||
|
|
@ -51,6 +63,14 @@ def issue_queryset_grouper(
|
||||||
IssueAssignee.objects.filter(
|
IssueAssignee.objects.filter(
|
||||||
issue_id=OuterRef("pk"),
|
issue_id=OuterRef("pk"),
|
||||||
deleted_at__isnull=True,
|
deleted_at__isnull=True,
|
||||||
|
assignee__is_active=True,
|
||||||
|
assignee__member_workspace__workspace_id=OuterRef("workspace_id"),
|
||||||
|
assignee__member_workspace__is_active=True,
|
||||||
|
assignee__member_workspace__is_banned=False,
|
||||||
|
assignee__member_workspace__deleted_at__isnull=True,
|
||||||
|
assignee__member_project__project_id=OuterRef("project_id"),
|
||||||
|
assignee__member_project__is_active=True,
|
||||||
|
assignee__member_project__deleted_at__isnull=True,
|
||||||
)
|
)
|
||||||
.values("issue_id")
|
.values("issue_id")
|
||||||
.annotate(arr=ArrayAgg("assignee_id", distinct=True))
|
.annotate(arr=ArrayAgg("assignee_id", distinct=True))
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,10 @@ export const InternalContourKanbanCard = observer(function InternalContourKanban
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { isMobile } = usePlatformOS();
|
const { isMobile } = usePlatformOS();
|
||||||
const { workspaceSlug: routerWorkspaceSlug } = useParams();
|
const { workspaceSlug: routerWorkspaceSlug } = useParams();
|
||||||
const { getUserDetails } = useMember();
|
const {
|
||||||
|
getUserDetails,
|
||||||
|
project: { getProjectMemberIds },
|
||||||
|
} = useMember();
|
||||||
const { getProjectById } = useProject();
|
const { getProjectById } = useProject();
|
||||||
const { getStateById, getProjectStateIds } = useProjectState();
|
const { getStateById, getProjectStateIds } = useProjectState();
|
||||||
|
|
||||||
|
|
@ -86,7 +89,11 @@ export const InternalContourKanbanCard = observer(function InternalContourKanban
|
||||||
const checkerItemsTotal = issue.checker_items_count ?? 0;
|
const checkerItemsTotal = issue.checker_items_count ?? 0;
|
||||||
const checkerItemsCompleted = issue.checker_items_completed_count ?? 0;
|
const checkerItemsCompleted = issue.checker_items_completed_count ?? 0;
|
||||||
const hasCheckerProgress = checkerBlocksTotal > 0;
|
const hasCheckerProgress = checkerBlocksTotal > 0;
|
||||||
const assigneeIds = issue.assignee_ids ?? [];
|
const rawAssigneeIds = issue.assignee_ids ?? [];
|
||||||
|
const projectMemberIds = issue.project_id ? getProjectMemberIds(issue.project_id, true) : null;
|
||||||
|
const assigneeIds = projectMemberIds
|
||||||
|
? rawAssigneeIds.filter((assigneeId) => projectMemberIds.includes(assigneeId))
|
||||||
|
: rawAssigneeIds;
|
||||||
const visibleAssigneeIds = assigneeIds.slice(0, 3);
|
const visibleAssigneeIds = assigneeIds.slice(0, 3);
|
||||||
const assigneeCount = assigneeIds.length;
|
const assigneeCount = assigneeIds.length;
|
||||||
const assigneeStackWidthClass =
|
const assigneeStackWidthClass =
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue