Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 79cb0d3860 | |||
| e7c84e86a9 | |||
| ad9be51eb4 | |||
| c74377fdfb | |||
| d0fe1ba284 | |||
| 60d37dde2f | |||
| c9a23d5b21 | |||
| f7debcde79 | |||
| 3de76206b5 | |||
| f783447796 | |||
| fa1b4a102a | |||
| 8399f64bee | |||
| 5f3f9d2623 | |||
| 110dbd9acd | |||
| b83d460938 | |||
| ea1f92e0c6 | |||
| 3d5e427894 | |||
| 6129e8e53b | |||
| d61b157929 | |||
| a37e5e2f6a | |||
| a976bea5af | |||
| 1072509642 | |||
| dcd8d27eae |
@@ -20,6 +20,12 @@ http {
|
||||
server {
|
||||
listen 3000;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "DENY" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
"react-dom": "catalog:",
|
||||
"react-hook-form": "7.51.5",
|
||||
"react-router": "catalog:",
|
||||
"react-router-dom": "catalog:",
|
||||
"serve": "14.2.5",
|
||||
"swr": "catalog:",
|
||||
"uuid": "catalog:"
|
||||
|
||||
@@ -18,7 +18,7 @@ from drf_spectacular.utils import OpenApiResponse, OpenApiRequest
|
||||
from plane.db.models import (
|
||||
Cycle,
|
||||
Intake,
|
||||
IssueUserProperty,
|
||||
ProjectUserProperty,
|
||||
Module,
|
||||
Project,
|
||||
DeployBoard,
|
||||
@@ -218,8 +218,6 @@ class ProjectListCreateAPIEndpoint(BaseAPIView):
|
||||
|
||||
# Add the user as Administrator to the project
|
||||
_ = ProjectMember.objects.create(project_id=serializer.instance.id, member=request.user, role=20)
|
||||
# Also create the issue property for the user
|
||||
_ = IssueUserProperty.objects.create(project_id=serializer.instance.id, user=request.user)
|
||||
|
||||
if serializer.instance.project_lead is not None and str(serializer.instance.project_lead) != str(
|
||||
request.user.id
|
||||
@@ -229,11 +227,6 @@ class ProjectListCreateAPIEndpoint(BaseAPIView):
|
||||
member_id=serializer.instance.project_lead,
|
||||
role=20,
|
||||
)
|
||||
# Also create the issue property for the user
|
||||
IssueUserProperty.objects.create(
|
||||
project_id=serializer.instance.id,
|
||||
user_id=serializer.instance.project_lead,
|
||||
)
|
||||
|
||||
State.objects.bulk_create(
|
||||
[
|
||||
|
||||
@@ -52,7 +52,7 @@ from .issue import (
|
||||
IssueCreateSerializer,
|
||||
IssueActivitySerializer,
|
||||
IssueCommentSerializer,
|
||||
IssueUserPropertySerializer,
|
||||
ProjectUserPropertySerializer,
|
||||
IssueAssigneeSerializer,
|
||||
LabelSerializer,
|
||||
IssueSerializer,
|
||||
|
||||
@@ -18,7 +18,7 @@ from plane.db.models import (
|
||||
Issue,
|
||||
IssueActivity,
|
||||
IssueComment,
|
||||
IssueUserProperty,
|
||||
ProjectUserProperty,
|
||||
IssueAssignee,
|
||||
IssueSubscriber,
|
||||
IssueLabel,
|
||||
@@ -346,9 +346,9 @@ class IssueActivitySerializer(BaseSerializer):
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class IssueUserPropertySerializer(BaseSerializer):
|
||||
class ProjectUserPropertySerializer(BaseSerializer):
|
||||
class Meta:
|
||||
model = IssueUserProperty
|
||||
model = ProjectUserProperty
|
||||
fields = "__all__"
|
||||
read_only_fields = ["user", "workspace", "project"]
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ from plane.app.views import (
|
||||
IssueReactionViewSet,
|
||||
IssueRelationViewSet,
|
||||
IssueSubscriberViewSet,
|
||||
IssueUserDisplayPropertyEndpoint,
|
||||
ProjectUserDisplayPropertyEndpoint,
|
||||
IssueViewSet,
|
||||
LabelViewSet,
|
||||
BulkArchiveIssuesEndpoint,
|
||||
@@ -208,13 +208,13 @@ urlpatterns = [
|
||||
name="project-issue-comment-reactions",
|
||||
),
|
||||
## End Comment Reactions
|
||||
## IssueUserProperty
|
||||
## ProjectUserProperty
|
||||
path(
|
||||
"workspaces/<str:slug>/projects/<uuid:project_id>/user-properties/",
|
||||
IssueUserDisplayPropertyEndpoint.as_view(),
|
||||
ProjectUserDisplayPropertyEndpoint.as_view(),
|
||||
name="project-issue-display-properties",
|
||||
),
|
||||
## IssueUserProperty End
|
||||
## ProjectUserProperty End
|
||||
## Issue Archives
|
||||
path(
|
||||
"workspaces/<str:slug>/projects/<uuid:project_id>/archived-issues/",
|
||||
|
||||
@@ -114,7 +114,7 @@ from .asset.v2 import (
|
||||
from .issue.base import (
|
||||
IssueListEndpoint,
|
||||
IssueViewSet,
|
||||
IssueUserDisplayPropertyEndpoint,
|
||||
ProjectUserDisplayPropertyEndpoint,
|
||||
BulkDeleteIssuesEndpoint,
|
||||
DeletedIssuesListViewSet,
|
||||
IssuePaginatedViewSet,
|
||||
|
||||
@@ -34,7 +34,7 @@ from plane.app.serializers import (
|
||||
IssueDetailSerializer,
|
||||
IssueListDetailSerializer,
|
||||
IssueSerializer,
|
||||
IssueUserPropertySerializer,
|
||||
ProjectUserPropertySerializer,
|
||||
)
|
||||
from plane.bgtasks.issue_activities_task import issue_activity
|
||||
from plane.bgtasks.issue_description_version_task import issue_description_version_task
|
||||
@@ -51,7 +51,7 @@ from plane.db.models import (
|
||||
IssueReaction,
|
||||
IssueRelation,
|
||||
IssueSubscriber,
|
||||
IssueUserProperty,
|
||||
ProjectUserProperty,
|
||||
ModuleIssue,
|
||||
Project,
|
||||
ProjectMember,
|
||||
@@ -723,23 +723,33 @@ class IssueViewSet(BaseViewSet):
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
|
||||
class IssueUserDisplayPropertyEndpoint(BaseAPIView):
|
||||
class ProjectUserDisplayPropertyEndpoint(BaseAPIView):
|
||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||
def patch(self, request, slug, project_id):
|
||||
issue_property = IssueUserProperty.objects.get(user=request.user, project_id=project_id)
|
||||
try:
|
||||
issue_property = ProjectUserProperty.objects.get(
|
||||
user=request.user,
|
||||
project_id=project_id
|
||||
)
|
||||
except ProjectUserProperty.DoesNotExist:
|
||||
issue_property = ProjectUserProperty.objects.create(
|
||||
user=request.user,
|
||||
project_id=project_id
|
||||
)
|
||||
|
||||
issue_property.rich_filters = request.data.get("rich_filters", issue_property.rich_filters)
|
||||
issue_property.filters = request.data.get("filters", issue_property.filters)
|
||||
issue_property.display_filters = request.data.get("display_filters", issue_property.display_filters)
|
||||
issue_property.display_properties = request.data.get("display_properties", issue_property.display_properties)
|
||||
issue_property.save()
|
||||
serializer = IssueUserPropertySerializer(issue_property)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
serializer = ProjectUserPropertySerializer(
|
||||
issue_property,
|
||||
data=request.data,
|
||||
partial=True
|
||||
)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||
def get(self, request, slug, project_id):
|
||||
issue_property, _ = IssueUserProperty.objects.get_or_create(user=request.user, project_id=project_id)
|
||||
serializer = IssueUserPropertySerializer(issue_property)
|
||||
issue_property, _ = ProjectUserProperty.objects.get_or_create(user=request.user, project_id=project_id)
|
||||
serializer = ProjectUserPropertySerializer(issue_property)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
|
||||
@@ -24,14 +24,15 @@ from plane.bgtasks.webhook_task import model_activity, webhook_activity
|
||||
from plane.db.models import (
|
||||
UserFavorite,
|
||||
DeployBoard,
|
||||
ProjectUserProperty,
|
||||
Intake,
|
||||
IssueUserProperty,
|
||||
Project,
|
||||
ProjectIdentifier,
|
||||
ProjectMember,
|
||||
ProjectNetwork,
|
||||
State,
|
||||
DEFAULT_STATES,
|
||||
UserFavorite,
|
||||
Workspace,
|
||||
WorkspaceMember,
|
||||
)
|
||||
@@ -250,8 +251,6 @@ class ProjectViewSet(BaseViewSet):
|
||||
member=request.user,
|
||||
role=ROLE.ADMIN.value,
|
||||
)
|
||||
# Also create the issue property for the user
|
||||
_ = IssueUserProperty.objects.create(project_id=serializer.data["id"], user=request.user)
|
||||
|
||||
if serializer.data["project_lead"] is not None and str(serializer.data["project_lead"]) != str(
|
||||
request.user.id
|
||||
@@ -261,11 +260,6 @@ class ProjectViewSet(BaseViewSet):
|
||||
member_id=serializer.data["project_lead"],
|
||||
role=ROLE.ADMIN.value,
|
||||
)
|
||||
# Also create the issue property for the user
|
||||
IssueUserProperty.objects.create(
|
||||
project_id=serializer.data["id"],
|
||||
user_id=serializer.data["project_lead"],
|
||||
)
|
||||
|
||||
State.objects.bulk_create(
|
||||
[
|
||||
|
||||
@@ -24,7 +24,7 @@ from plane.db.models import (
|
||||
User,
|
||||
WorkspaceMember,
|
||||
Project,
|
||||
IssueUserProperty,
|
||||
ProjectUserProperty,
|
||||
)
|
||||
from plane.db.models.project import ProjectNetwork
|
||||
from plane.utils.host import base_host
|
||||
@@ -160,9 +160,9 @@ class UserProjectInvitationsViewset(BaseViewSet):
|
||||
ignore_conflicts=True,
|
||||
)
|
||||
|
||||
IssueUserProperty.objects.bulk_create(
|
||||
ProjectUserProperty.objects.bulk_create(
|
||||
[
|
||||
IssueUserProperty(
|
||||
ProjectUserProperty(
|
||||
project_id=project_id,
|
||||
user=request.user,
|
||||
workspace=workspace,
|
||||
@@ -220,7 +220,7 @@ class ProjectJoinEndpoint(BaseAPIView):
|
||||
if project_member is None:
|
||||
# Create a Project Member
|
||||
_ = ProjectMember.objects.create(
|
||||
workspace_id=project_invite.workspace_id,
|
||||
project_id=project_id,
|
||||
member=user,
|
||||
role=project_invite.role,
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Third Party imports
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from django.db.models import Min
|
||||
|
||||
# Module imports
|
||||
from .base import BaseViewSet, BaseAPIView
|
||||
@@ -13,7 +14,7 @@ from plane.app.serializers import (
|
||||
|
||||
from plane.app.permissions import WorkspaceUserPermission
|
||||
|
||||
from plane.db.models import Project, ProjectMember, IssueUserProperty, WorkspaceMember
|
||||
from plane.db.models import Project, ProjectMember, ProjectUserProperty, WorkspaceMember
|
||||
from plane.bgtasks.project_add_user_email_task import project_add_user_email
|
||||
from plane.utils.host import base_host
|
||||
from plane.app.permissions.base import allow_permission, ROLE
|
||||
@@ -89,24 +90,23 @@ class ProjectMemberViewSet(BaseViewSet):
|
||||
# Update the roles of the existing members
|
||||
ProjectMember.objects.bulk_update(bulk_project_members, ["is_active", "role"], batch_size=100)
|
||||
|
||||
# Get the list of project members of the requested workspace with the given slug
|
||||
project_members = (
|
||||
ProjectMember.objects.filter(
|
||||
# Get the minimum sort_order for each member in the workspace
|
||||
member_sort_orders = (
|
||||
ProjectUserProperty.objects.filter(
|
||||
workspace__slug=slug,
|
||||
member_id__in=[member.get("member_id") for member in members],
|
||||
user_id__in=[member.get("member_id") for member in members],
|
||||
)
|
||||
.values("member_id", "sort_order")
|
||||
.order_by("sort_order")
|
||||
.values("user_id")
|
||||
.annotate(min_sort_order=Min("sort_order"))
|
||||
)
|
||||
# Convert to dictionary for easy lookup: {user_id: min_sort_order}
|
||||
sort_order_map = {str(item["user_id"]): item["min_sort_order"] for item in member_sort_orders}
|
||||
|
||||
# Loop through requested members
|
||||
for member in members:
|
||||
# Get the sort orders of the member
|
||||
sort_order = [
|
||||
project_member.get("sort_order")
|
||||
for project_member in project_members
|
||||
if str(project_member.get("member_id")) == str(member.get("member_id"))
|
||||
]
|
||||
member_id = str(member.get("member_id"))
|
||||
# Get the minimum sort_order for this member, or use default
|
||||
min_sort_order = sort_order_map.get(member_id)
|
||||
# Create a new project member
|
||||
bulk_project_members.append(
|
||||
ProjectMember(
|
||||
@@ -114,22 +114,22 @@ class ProjectMemberViewSet(BaseViewSet):
|
||||
role=member.get("role", 5),
|
||||
project_id=project_id,
|
||||
workspace_id=project.workspace_id,
|
||||
sort_order=(sort_order[0] - 10000 if len(sort_order) else 65535),
|
||||
)
|
||||
)
|
||||
# Create a new issue property
|
||||
bulk_issue_props.append(
|
||||
IssueUserProperty(
|
||||
ProjectUserProperty(
|
||||
user_id=member.get("member_id"),
|
||||
project_id=project_id,
|
||||
workspace_id=project.workspace_id,
|
||||
sort_order=(min_sort_order - 10000 if min_sort_order is not None else 65535),
|
||||
)
|
||||
)
|
||||
|
||||
# Bulk create the project members and issue properties
|
||||
project_members = ProjectMember.objects.bulk_create(bulk_project_members, batch_size=10, ignore_conflicts=True)
|
||||
|
||||
_ = IssueUserProperty.objects.bulk_create(bulk_issue_props, batch_size=10, ignore_conflicts=True)
|
||||
_ = ProjectUserProperty.objects.bulk_create(bulk_issue_props, batch_size=10, ignore_conflicts=True)
|
||||
|
||||
project_members = ProjectMember.objects.filter(
|
||||
project_id=project_id,
|
||||
|
||||
@@ -77,14 +77,12 @@ class WorkSpaceViewSet(BaseViewSet):
|
||||
|
||||
def create(self, request):
|
||||
try:
|
||||
(DISABLE_WORKSPACE_CREATION,) = get_configuration_value(
|
||||
[
|
||||
{
|
||||
"key": "DISABLE_WORKSPACE_CREATION",
|
||||
"default": os.environ.get("DISABLE_WORKSPACE_CREATION", "0"),
|
||||
}
|
||||
]
|
||||
)
|
||||
(DISABLE_WORKSPACE_CREATION,) = get_configuration_value([
|
||||
{
|
||||
"key": "DISABLE_WORKSPACE_CREATION",
|
||||
"default": os.environ.get("DISABLE_WORKSPACE_CREATION", "0"),
|
||||
}
|
||||
])
|
||||
|
||||
if DISABLE_WORKSPACE_CREATION == "1":
|
||||
return Response(
|
||||
|
||||
@@ -21,7 +21,7 @@ from plane.db.models import (
|
||||
WorkspaceMember,
|
||||
Project,
|
||||
ProjectMember,
|
||||
IssueUserProperty,
|
||||
ProjectUserProperty,
|
||||
State,
|
||||
Label,
|
||||
Issue,
|
||||
@@ -94,7 +94,7 @@ def create_project_and_member(workspace: Workspace, bot_user: User) -> Dict[int,
|
||||
project_seed.pop("name", None)
|
||||
project_seed.pop("identifier", None)
|
||||
|
||||
project = Project.objects.create(
|
||||
project = Project(
|
||||
**project_seed,
|
||||
workspace=workspace,
|
||||
name=workspace.name, # Use workspace name
|
||||
@@ -105,6 +105,7 @@ def create_project_and_member(workspace: Workspace, bot_user: User) -> Dict[int,
|
||||
module_view=True,
|
||||
issue_views_view=True,
|
||||
)
|
||||
project.save(created_by_id=bot_user.id, disable_auto_set_user=True)
|
||||
|
||||
# Create project members
|
||||
ProjectMember.objects.bulk_create(
|
||||
@@ -121,9 +122,9 @@ def create_project_and_member(workspace: Workspace, bot_user: User) -> Dict[int,
|
||||
)
|
||||
|
||||
# Create issue user properties
|
||||
IssueUserProperty.objects.bulk_create(
|
||||
ProjectUserProperty.objects.bulk_create(
|
||||
[
|
||||
IssueUserProperty(
|
||||
ProjectUserProperty(
|
||||
project=project,
|
||||
user_id=workspace_member["member_id"],
|
||||
workspace_id=workspace.id,
|
||||
@@ -191,13 +192,13 @@ def create_project_states(
|
||||
state_id = state_seed.pop("id")
|
||||
project_id = state_seed.pop("project_id")
|
||||
|
||||
state = State.objects.create(
|
||||
state = State(
|
||||
**state_seed,
|
||||
project_id=project_map[project_id],
|
||||
workspace=workspace,
|
||||
created_by_id=bot_user.id,
|
||||
)
|
||||
|
||||
state.save(created_by_id=bot_user.id, disable_auto_set_user=True)
|
||||
state_map[state_id] = state.id
|
||||
logger.info(f"Task: workspace_seed_task -> State {state_id} created")
|
||||
return state_map
|
||||
@@ -224,12 +225,13 @@ def create_project_labels(
|
||||
for label_seed in label_seeds:
|
||||
label_id = label_seed.pop("id")
|
||||
project_id = label_seed.pop("project_id")
|
||||
label = Label.objects.create(
|
||||
label = Label(
|
||||
**label_seed,
|
||||
project_id=project_map[project_id],
|
||||
workspace=workspace,
|
||||
created_by_id=bot_user.id,
|
||||
)
|
||||
label.save(created_by_id=bot_user.id, disable_auto_set_user=True)
|
||||
label_map[label_id] = label.id
|
||||
|
||||
logger.info(f"Task: workspace_seed_task -> Label {label_id} created")
|
||||
@@ -276,13 +278,14 @@ def create_project_issues(
|
||||
cycle_id = issue_seed.pop("cycle_id")
|
||||
module_ids = issue_seed.pop("module_ids")
|
||||
|
||||
issue = Issue.objects.create(
|
||||
issue = Issue(
|
||||
**issue_seed,
|
||||
state_id=states_map[state_id],
|
||||
project_id=project_map[project_id],
|
||||
workspace=workspace,
|
||||
created_by_id=bot_user.id,
|
||||
)
|
||||
issue.save(created_by_id=bot_user.id, disable_auto_set_user=True)
|
||||
IssueSequence.objects.create(
|
||||
issue=issue,
|
||||
project_id=project_map[project_id],
|
||||
@@ -351,7 +354,7 @@ def create_pages(workspace: Workspace, project_map: Dict[int, uuid.UUID], bot_us
|
||||
for page_seed in page_seeds:
|
||||
page_id = page_seed.pop("id")
|
||||
|
||||
page = Page.objects.create(
|
||||
page = Page(
|
||||
workspace_id=workspace.id,
|
||||
is_global=False,
|
||||
access=page_seed.get("access", Page.PUBLIC_ACCESS),
|
||||
@@ -365,16 +368,18 @@ def create_pages(workspace: Workspace, project_map: Dict[int, uuid.UUID], bot_us
|
||||
owned_by_id=bot_user.id,
|
||||
)
|
||||
|
||||
page.save(created_by_id=bot_user.id, disable_auto_set_user=True)
|
||||
|
||||
logger.info(f"Task: workspace_seed_task -> Page {page_id} created")
|
||||
if page_seed.get("project_id") and page_seed.get("type") == "PROJECT":
|
||||
ProjectPage.objects.create(
|
||||
project_page = ProjectPage(
|
||||
workspace_id=workspace.id,
|
||||
project_id=project_map[page_seed.get("project_id")],
|
||||
page_id=page.id,
|
||||
created_by_id=bot_user.id,
|
||||
updated_by_id=bot_user.id,
|
||||
)
|
||||
|
||||
project_page.save(created_by_id=bot_user.id, disable_auto_set_user=True)
|
||||
logger.info(f"Task: workspace_seed_task -> Project Page {page_id} created")
|
||||
return
|
||||
|
||||
@@ -414,7 +419,7 @@ def create_cycles(workspace: Workspace, project_map: Dict[int, uuid.UUID], bot_u
|
||||
start_date = timezone.now() + timedelta(days=14)
|
||||
end_date = start_date + timedelta(days=14)
|
||||
|
||||
cycle = Cycle.objects.create(
|
||||
cycle = Cycle(
|
||||
**cycle_seed,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
@@ -423,6 +428,7 @@ def create_cycles(workspace: Workspace, project_map: Dict[int, uuid.UUID], bot_u
|
||||
created_by_id=bot_user.id,
|
||||
owned_by_id=bot_user.id,
|
||||
)
|
||||
cycle.save(created_by_id=bot_user.id, disable_auto_set_user=True)
|
||||
|
||||
cycle_map[cycle_id] = cycle.id
|
||||
logger.info(f"Task: workspace_seed_task -> Cycle {cycle_id} created")
|
||||
@@ -450,7 +456,7 @@ def create_modules(workspace: Workspace, project_map: Dict[int, uuid.UUID], bot_
|
||||
start_date = timezone.now() + timedelta(days=index * 2)
|
||||
end_date = start_date + timedelta(days=14)
|
||||
|
||||
module = Module.objects.create(
|
||||
module = Module(
|
||||
**module_seed,
|
||||
start_date=start_date,
|
||||
target_date=end_date,
|
||||
@@ -458,6 +464,7 @@ def create_modules(workspace: Workspace, project_map: Dict[int, uuid.UUID], bot_
|
||||
workspace=workspace,
|
||||
created_by_id=bot_user.id,
|
||||
)
|
||||
module.save(created_by_id=bot_user.id, disable_auto_set_user=True)
|
||||
module_map[module_id] = module.id
|
||||
logger.info(f"Task: workspace_seed_task -> Module {module_id} created")
|
||||
return module_map
|
||||
@@ -478,13 +485,15 @@ def create_views(workspace: Workspace, project_map: Dict[int, uuid.UUID], bot_us
|
||||
|
||||
for view_seed in view_seeds:
|
||||
project_id = view_seed.pop("project_id")
|
||||
IssueView.objects.create(
|
||||
view_seed.pop("id")
|
||||
issue_view = IssueView(
|
||||
**view_seed,
|
||||
project_id=project_map[project_id],
|
||||
workspace=workspace,
|
||||
created_by_id=bot_user.id,
|
||||
owned_by_id=bot_user.id,
|
||||
)
|
||||
issue_view.save(created_by_id=bot_user.id, disable_auto_set_user=True)
|
||||
|
||||
|
||||
@shared_task
|
||||
@@ -518,6 +527,14 @@ def workspace_seed(workspace_id: uuid.UUID) -> None:
|
||||
is_password_autoset=True,
|
||||
)
|
||||
|
||||
# Add bot user to workspace as member
|
||||
WorkspaceMember.objects.create(
|
||||
workspace=workspace,
|
||||
member=bot_user,
|
||||
role=20,
|
||||
company_role="",
|
||||
)
|
||||
|
||||
# Create a project with the same name as workspace
|
||||
project_map = create_project_and_member(workspace, bot_user)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ from plane.db.models import (
|
||||
WorkspaceMember,
|
||||
ProjectMember,
|
||||
Project,
|
||||
IssueUserProperty,
|
||||
ProjectUserProperty,
|
||||
)
|
||||
|
||||
|
||||
@@ -47,27 +47,18 @@ class Command(BaseCommand):
|
||||
if not WorkspaceMember.objects.filter(workspace=project.workspace, member=user, is_active=True).exists():
|
||||
raise CommandError("User not member in workspace")
|
||||
|
||||
# Get the smallest sort order
|
||||
smallest_sort_order = (
|
||||
ProjectMember.objects.filter(workspace_id=project.workspace_id).order_by("sort_order").first()
|
||||
)
|
||||
|
||||
if smallest_sort_order:
|
||||
sort_order = smallest_sort_order.sort_order - 1000
|
||||
else:
|
||||
sort_order = 65535
|
||||
|
||||
if ProjectMember.objects.filter(project=project, member=user).exists():
|
||||
# Update the project member
|
||||
ProjectMember.objects.filter(project=project, member=user).update(
|
||||
is_active=True, sort_order=sort_order, role=role
|
||||
is_active=True, role=role
|
||||
)
|
||||
else:
|
||||
# Create the project member
|
||||
ProjectMember.objects.create(project=project, member=user, role=role, sort_order=sort_order)
|
||||
ProjectMember.objects.create(project=project, member=user, role=role)
|
||||
|
||||
# Issue Property
|
||||
IssueUserProperty.objects.get_or_create(user=user, project=project)
|
||||
ProjectUserProperty.objects.get_or_create(user=user, project=project)
|
||||
|
||||
# Success message
|
||||
self.stdout.write(self.style.SUCCESS(f"User {user_email} added to project {project_id}"))
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# Generated by Django 4.2.26 on 2025-12-15 10:29
|
||||
|
||||
from django.db import migrations, models
|
||||
import plane.db.models.workspace
|
||||
|
||||
|
||||
def get_default_product_tour():
|
||||
return {
|
||||
"work_items": True,
|
||||
"cycles": True,
|
||||
"modules": True,
|
||||
"intake": True,
|
||||
"pages": True,
|
||||
}
|
||||
|
||||
|
||||
def populate_product_tour(apps, _schema_editor):
|
||||
WorkspaceUserProperties = apps.get_model('db', 'WorkspaceUserProperties')
|
||||
default_value = get_default_product_tour()
|
||||
# Use bulk update for better performance
|
||||
WorkspaceUserProperties.objects.all().update(product_tour=default_value)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('db', '0112_auto_20251124_0603'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='webhook',
|
||||
name='version',
|
||||
field=models.CharField(default='v1', max_length=50),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='profile',
|
||||
name='is_navigation_tour_completed',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspaceuserproperties',
|
||||
name='product_tour',
|
||||
field=models.JSONField(default=plane.db.models.workspace.get_default_product_tour),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='apitoken',
|
||||
name='allowed_rate_limit',
|
||||
field=models.CharField(default='60/min', max_length=255),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='profile',
|
||||
name='is_subscribed_to_changelog',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.RunPython(populate_product_tour, reverse_code=migrations.RunPython.noop),
|
||||
]
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
# Generated by Django 4.2.22 on 2026-01-05 08:35
|
||||
|
||||
from django.db import migrations, models
|
||||
import plane.db.models.project
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('db', '0113_webhook_version'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelTable(
|
||||
name='issueuserproperty',
|
||||
table='project_user_properties',
|
||||
),
|
||||
migrations.RenameModel(
|
||||
old_name='IssueUserProperty',
|
||||
new_name='ProjectUserProperty',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='projectuserproperty',
|
||||
name='preferences',
|
||||
field=models.JSONField(default=plane.db.models.project.get_default_preferences),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='projectuserproperty',
|
||||
name='sort_order',
|
||||
field=models.FloatField(default=65535),
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='projectuserproperty',
|
||||
options={'ordering': ('-created_at',), 'verbose_name': 'Project User Property', 'verbose_name_plural': 'Project User Properties'},
|
||||
),
|
||||
migrations.RemoveConstraint(
|
||||
model_name='projectuserproperty',
|
||||
name='issue_user_property_unique_user_project_when_deleted_at_null',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='projectuserproperty',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_property_user', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='projectuserproperty',
|
||||
constraint=models.UniqueConstraint(condition=models.Q(('deleted_at__isnull', True)), fields=('user', 'project'), name='project_user_property_unique_user_project_when_deleted_at_null'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,51 @@
|
||||
# Generated by Django 4.2.22 on 2026-01-05 08:36
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
def move_issue_user_properties_to_project_user_properties(apps, schema_editor):
|
||||
ProjectMember = apps.get_model('db', 'ProjectMember')
|
||||
ProjectUserProperty = apps.get_model('db', 'ProjectUserProperty')
|
||||
|
||||
# Get all project members
|
||||
project_members = ProjectMember.objects.filter(deleted_at__isnull=True).values('member_id', 'project_id', 'preferences', 'sort_order')
|
||||
|
||||
# create a mapping with consistent ordering
|
||||
pm_dict = {
|
||||
(pm['member_id'], pm['project_id']): pm
|
||||
for pm in project_members
|
||||
}
|
||||
|
||||
# Get all project user properties
|
||||
properties_to_update = []
|
||||
for projectuserproperty in ProjectUserProperty.objects.filter(deleted_at__isnull=True):
|
||||
pm = pm_dict.get((projectuserproperty.user_id, projectuserproperty.project_id))
|
||||
if pm:
|
||||
projectuserproperty.preferences = pm['preferences']
|
||||
projectuserproperty.sort_order = pm['sort_order']
|
||||
properties_to_update.append(projectuserproperty)
|
||||
|
||||
ProjectUserProperty.objects.bulk_update(properties_to_update, ['preferences', 'sort_order'], batch_size=2000)
|
||||
|
||||
|
||||
|
||||
def migrate_existing_api_tokens(apps, schema_editor):
|
||||
APIToken = apps.get_model('db', 'APIToken')
|
||||
|
||||
# Update all the existing non-service api tokens to not have a workspace
|
||||
APIToken.objects.filter(is_service=False, user__is_bot=False).update(
|
||||
workspace_id=None,
|
||||
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('db', '0114_projectuserproperty_delete_issueuserproperty_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(move_issue_user_properties_to_project_user_properties, reverse_code=migrations.RunPython.noop),
|
||||
migrations.RunPython(migrate_existing_api_tokens, reverse_code=migrations.RunPython.noop),
|
||||
]
|
||||
@@ -0,0 +1,38 @@
|
||||
# Generated by Django 4.2.27 on 2026-01-13 10:38
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('db', '0115_auto_20260105_1406'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='profile',
|
||||
name='notification_view_mode',
|
||||
field=models.CharField(choices=[('full', 'Full'), ('compact', 'Compact')], default='full', max_length=255),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='is_password_reset_required',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspacemember',
|
||||
name='explored_features',
|
||||
field=models.JSONField(default=dict),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspacemember',
|
||||
name='getting_started_checklist',
|
||||
field=models.JSONField(default=dict),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspacemember',
|
||||
name='tips',
|
||||
field=models.JSONField(default=dict),
|
||||
),
|
||||
]
|
||||
@@ -34,7 +34,6 @@ from .issue import (
|
||||
IssueLabel,
|
||||
IssueLink,
|
||||
IssueMention,
|
||||
IssueUserProperty,
|
||||
IssueReaction,
|
||||
IssueRelation,
|
||||
IssueSequence,
|
||||
@@ -54,6 +53,7 @@ from .project import (
|
||||
ProjectMemberInvite,
|
||||
ProjectNetwork,
|
||||
ProjectPublicMember,
|
||||
ProjectUserProperty,
|
||||
)
|
||||
from .session import Session
|
||||
from .social_connection import SocialLoginConnection
|
||||
|
||||
@@ -32,6 +32,7 @@ class APIToken(BaseModel):
|
||||
workspace = models.ForeignKey("db.Workspace", related_name="api_tokens", on_delete=models.CASCADE, null=True)
|
||||
expired_at = models.DateTimeField(blank=True, null=True)
|
||||
is_service = models.BooleanField(default=False)
|
||||
allowed_rate_limit = models.CharField(max_length=255, default="60/min")
|
||||
|
||||
class Meta:
|
||||
verbose_name = "API Token"
|
||||
|
||||
@@ -526,36 +526,6 @@ class IssueComment(ChangeTrackerMixin, ProjectBaseModel):
|
||||
return str(self.issue)
|
||||
|
||||
|
||||
class IssueUserProperty(ProjectBaseModel):
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="issue_property_user",
|
||||
)
|
||||
filters = models.JSONField(default=get_default_filters)
|
||||
display_filters = models.JSONField(default=get_default_display_filters)
|
||||
display_properties = models.JSONField(default=get_default_display_properties)
|
||||
rich_filters = models.JSONField(default=dict)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Issue User Property"
|
||||
verbose_name_plural = "Issue User Properties"
|
||||
db_table = "issue_user_properties"
|
||||
ordering = ("-created_at",)
|
||||
unique_together = ["user", "project", "deleted_at"]
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=["user", "project"],
|
||||
condition=Q(deleted_at__isnull=True),
|
||||
name="issue_user_property_unique_user_project_when_deleted_at_null",
|
||||
)
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
"""Return properties status of the issue"""
|
||||
return str(self.user)
|
||||
|
||||
|
||||
class IssueLabel(ProjectBaseModel):
|
||||
issue = models.ForeignKey("db.Issue", on_delete=models.CASCADE, related_name="label_issue")
|
||||
label = models.ForeignKey("db.Label", on_delete=models.CASCADE, related_name="label_issue")
|
||||
|
||||
@@ -12,7 +12,6 @@ from django.db.models import Q
|
||||
# Module imports
|
||||
from plane.db.mixins import AuditModel
|
||||
|
||||
# Module imports
|
||||
from .base import BaseModel
|
||||
|
||||
ROLE_CHOICES = ((20, "Admin"), (15, "Member"), (5, "Guest"))
|
||||
@@ -219,14 +218,20 @@ class ProjectMember(ProjectBaseModel):
|
||||
is_active = models.BooleanField(default=True)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self._state.adding:
|
||||
smallest_sort_order = ProjectMember.objects.filter(
|
||||
workspace_id=self.project.workspace_id, member=self.member
|
||||
).aggregate(smallest=models.Min("sort_order"))["smallest"]
|
||||
if self._state.adding and self.member:
|
||||
# Get the minimum sort_order for this member in the workspace
|
||||
min_sort_order_result = ProjectUserProperty.objects.filter(
|
||||
workspace_id=self.project.workspace_id, user=self.member
|
||||
).aggregate(min_sort_order=models.Min("sort_order"))
|
||||
min_sort_order = min_sort_order_result.get("min_sort_order")
|
||||
|
||||
# Project ordering
|
||||
if smallest_sort_order is not None:
|
||||
self.sort_order = smallest_sort_order - 10000
|
||||
# create project user property with project sort order
|
||||
ProjectUserProperty.objects.create(
|
||||
workspace_id=self.project.workspace_id,
|
||||
project=self.project,
|
||||
user=self.member,
|
||||
sort_order=(min_sort_order - 10000 if min_sort_order is not None else 65535),
|
||||
)
|
||||
|
||||
super(ProjectMember, self).save(*args, **kwargs)
|
||||
|
||||
@@ -326,3 +331,37 @@ class ProjectPublicMember(ProjectBaseModel):
|
||||
verbose_name_plural = "Project Public Members"
|
||||
db_table = "project_public_members"
|
||||
ordering = ("-created_at",)
|
||||
|
||||
|
||||
class ProjectUserProperty(ProjectBaseModel):
|
||||
from .issue import get_default_filters, get_default_display_filters, get_default_display_properties
|
||||
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="project_property_user",
|
||||
)
|
||||
filters = models.JSONField(default=get_default_filters)
|
||||
display_filters = models.JSONField(default=get_default_display_filters)
|
||||
display_properties = models.JSONField(default=get_default_display_properties)
|
||||
rich_filters = models.JSONField(default=dict)
|
||||
preferences = models.JSONField(default=get_default_preferences)
|
||||
sort_order = models.FloatField(default=65535)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Project User Property"
|
||||
verbose_name_plural = "Project User Properties"
|
||||
db_table = "project_user_properties"
|
||||
ordering = ("-created_at",)
|
||||
unique_together = ["user", "project", "deleted_at"]
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=["user", "project"],
|
||||
condition=Q(deleted_at__isnull=True),
|
||||
name="project_user_property_unique_user_project_when_deleted_at_null",
|
||||
)
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
"""Return properties status of the project"""
|
||||
return str(self.user)
|
||||
|
||||
@@ -84,7 +84,7 @@ class User(AbstractBaseUser, PermissionsMixin):
|
||||
is_staff = models.BooleanField(default=False)
|
||||
is_email_verified = models.BooleanField(default=False)
|
||||
is_password_autoset = models.BooleanField(default=False)
|
||||
|
||||
is_password_reset_required = models.BooleanField(default=False)
|
||||
# random token generated
|
||||
token = models.CharField(max_length=64, blank=True)
|
||||
|
||||
@@ -192,6 +192,10 @@ class Profile(TimeAuditModel):
|
||||
FRIDAY = 5
|
||||
SATURDAY = 6
|
||||
|
||||
class NotificationViewMode(models.TextChoices):
|
||||
FULL = "full", "Full"
|
||||
COMPACT = "compact", "Compact"
|
||||
|
||||
START_OF_THE_WEEK_CHOICES = (
|
||||
(SUNDAY, "Sunday"),
|
||||
(MONDAY, "Monday"),
|
||||
@@ -221,7 +225,9 @@ class Profile(TimeAuditModel):
|
||||
billing_address = models.JSONField(null=True)
|
||||
has_billing_address = models.BooleanField(default=False)
|
||||
company_name = models.CharField(max_length=255, blank=True)
|
||||
|
||||
notification_view_mode = models.CharField(
|
||||
max_length=255, choices=NotificationViewMode.choices, default=NotificationViewMode.FULL
|
||||
)
|
||||
is_smooth_cursor_enabled = models.BooleanField(default=False)
|
||||
# mobile
|
||||
is_mobile_onboarded = models.BooleanField(default=False)
|
||||
@@ -233,8 +239,12 @@ class Profile(TimeAuditModel):
|
||||
goals = models.JSONField(default=dict)
|
||||
background_color = models.CharField(max_length=255, default=get_random_color)
|
||||
|
||||
# navigation tour
|
||||
is_navigation_tour_completed = models.BooleanField(default=False)
|
||||
|
||||
# marketing
|
||||
has_marketing_email_consent = models.BooleanField(default=False)
|
||||
is_subscribed_to_changelog = models.BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Profile"
|
||||
|
||||
@@ -38,6 +38,7 @@ class Webhook(BaseModel):
|
||||
cycle = models.BooleanField(default=False)
|
||||
issue_comment = models.BooleanField(default=False)
|
||||
is_internal = models.BooleanField(default=False)
|
||||
version = models.CharField(default="v1", max_length=50)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.workspace.slug} {self.url}"
|
||||
|
||||
@@ -112,6 +112,16 @@ def slug_validator(value):
|
||||
raise ValidationError("Slug is not valid")
|
||||
|
||||
|
||||
def get_default_product_tour():
|
||||
return {
|
||||
"work_items": False,
|
||||
"cycles": False,
|
||||
"modules": False,
|
||||
"intake": False,
|
||||
"pages": False,
|
||||
}
|
||||
|
||||
|
||||
class Workspace(BaseModel):
|
||||
TIMEZONE_CHOICES = tuple(zip(pytz.common_timezones, pytz.common_timezones))
|
||||
|
||||
@@ -204,6 +214,9 @@ class WorkspaceMember(BaseModel):
|
||||
default_props = models.JSONField(default=get_default_props)
|
||||
issue_props = models.JSONField(default=get_issue_props)
|
||||
is_active = models.BooleanField(default=True)
|
||||
getting_started_checklist = models.JSONField(default=dict)
|
||||
tips = models.JSONField(default=dict)
|
||||
explored_features = models.JSONField(default=dict)
|
||||
|
||||
class Meta:
|
||||
unique_together = ["workspace", "member", "deleted_at"]
|
||||
@@ -325,6 +338,7 @@ class WorkspaceUserProperties(BaseModel):
|
||||
choices=NavigationControlPreference.choices,
|
||||
default=NavigationControlPreference.ACCORDION,
|
||||
)
|
||||
product_tour = models.JSONField(default=get_default_product_tour)
|
||||
|
||||
class Meta:
|
||||
unique_together = ["workspace", "user", "deleted_at"]
|
||||
|
||||
@@ -6,7 +6,7 @@ from django.utils import timezone
|
||||
from plane.db.models import (
|
||||
Project,
|
||||
ProjectMember,
|
||||
IssueUserProperty,
|
||||
ProjectUserProperty,
|
||||
State,
|
||||
WorkspaceMember,
|
||||
User,
|
||||
@@ -82,8 +82,8 @@ class TestProjectAPIPost(TestProjectBase):
|
||||
assert project_member.role == 20 # Administrator
|
||||
assert project_member.is_active is True
|
||||
|
||||
# Verify IssueUserProperty was created
|
||||
assert IssueUserProperty.objects.filter(project=project, user=user).exists()
|
||||
# Verify ProjectUserProperty was created
|
||||
assert ProjectUserProperty.objects.filter(project=project, user=user).exists()
|
||||
|
||||
# Verify default states were created
|
||||
states = State.objects.filter(project=project)
|
||||
@@ -116,8 +116,8 @@ class TestProjectAPIPost(TestProjectBase):
|
||||
project = Project.objects.get(name=project_data["name"])
|
||||
assert ProjectMember.objects.filter(project=project, role=20).count() == 2
|
||||
|
||||
# Verify both have IssueUserProperty
|
||||
assert IssueUserProperty.objects.filter(project=project).count() == 2
|
||||
# Verify both have ProjectUserProperty
|
||||
assert ProjectUserProperty.objects.filter(project=project).count() == 2
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_create_project_guest_forbidden(self, session_client, workspace):
|
||||
|
||||
@@ -98,7 +98,7 @@ export const AuthRoot = observer(function AuthRoot() {
|
||||
}
|
||||
|
||||
if (currentAuthMode === EAuthModes.SIGN_IN) {
|
||||
if (response.is_password_autoset && isSMTPConfigured && isMagicLoginEnabled) {
|
||||
if (isSMTPConfigured && isMagicLoginEnabled && response.status === "MAGIC_CODE") {
|
||||
setAuthStep(EAuthSteps.UNIQUE_CODE);
|
||||
generateEmailUniqueCode(data.email);
|
||||
} else if (isEmailPasswordEnabled) {
|
||||
@@ -109,7 +109,7 @@ export const AuthRoot = observer(function AuthRoot() {
|
||||
setErrorInfo(errorhandler);
|
||||
}
|
||||
} else {
|
||||
if (isSMTPConfigured && isMagicLoginEnabled) {
|
||||
if (isSMTPConfigured && isMagicLoginEnabled && response.status === "MAGIC_CODE") {
|
||||
setAuthStep(EAuthSteps.UNIQUE_CODE);
|
||||
generateEmailUniqueCode(data.email);
|
||||
} else if (isEmailPasswordEnabled) {
|
||||
@@ -119,6 +119,7 @@ export const AuthRoot = observer(function AuthRoot() {
|
||||
setErrorInfo(errorhandler);
|
||||
}
|
||||
}
|
||||
return;
|
||||
})
|
||||
.catch((error) => {
|
||||
const errorhandler = authErrorHandler(error?.error_code?.toString(), data?.email || undefined);
|
||||
|
||||
@@ -19,6 +19,7 @@ export interface IEmailCheckData {
|
||||
}
|
||||
|
||||
export interface IEmailCheckResponse {
|
||||
status: "MAGIC_CODE" | "CREDENTIAL";
|
||||
is_password_autoset: boolean;
|
||||
existing: boolean;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"react-hook-form": "7.51.5",
|
||||
"react-popper": "^2.3.0",
|
||||
"react-router": "catalog:",
|
||||
"react-router-dom": "catalog:",
|
||||
"swr": "catalog:",
|
||||
"uuid": "catalog:"
|
||||
},
|
||||
|
||||
@@ -2,16 +2,15 @@ import { useState, useEffect } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
// plane package imports
|
||||
import { EUserPermissions, EUserPermissionsLevel, PROJECT_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { EmptyStateDetailed } from "@plane/propel/empty-state";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@plane/propel/tabs";
|
||||
import { Tabs } from "@plane/propel/tabs";
|
||||
// components
|
||||
import { cn } from "@plane/utils";
|
||||
import AnalyticsFilterActions from "@/components/analytics/analytics-filter-actions";
|
||||
import { PageHead } from "@/components/core/page-title";
|
||||
// hooks
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
import { useCommandPalette } from "@/hooks/store/use-command-palette";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
@@ -75,9 +74,9 @@ function AnalyticsPage({ params }: Route.ComponentProps) {
|
||||
"px-6 py-2 border-b border-subtle flex items-center gap-4 overflow-hidden w-full justify-between bg-surface-1"
|
||||
)}
|
||||
>
|
||||
<TabsList className={"overflow-x-auto flex w-fit h-7"}>
|
||||
<Tabs.List className={"overflow-x-auto flex w-fit h-7"}>
|
||||
{ANALYTICS_TABS.map((tab) => (
|
||||
<TabsTrigger
|
||||
<Tabs.Trigger
|
||||
key={tab.key}
|
||||
value={tab.key}
|
||||
disabled={tab.isDisabled}
|
||||
@@ -90,22 +89,22 @@ function AnalyticsPage({ params }: Route.ComponentProps) {
|
||||
}}
|
||||
>
|
||||
{tab.label}
|
||||
</TabsTrigger>
|
||||
</Tabs.Trigger>
|
||||
))}
|
||||
</TabsList>
|
||||
</Tabs.List>
|
||||
|
||||
<div className="flex-shrink-0">
|
||||
<AnalyticsFilterActions />
|
||||
</div>
|
||||
</div>
|
||||
{ANALYTICS_TABS.map((tab) => (
|
||||
<TabsContent
|
||||
<Tabs.Content
|
||||
key={tab.key}
|
||||
value={tab.key}
|
||||
className={"h-full overflow-hidden overflow-y-auto px-2"}
|
||||
>
|
||||
<tab.content />
|
||||
</TabsContent>
|
||||
</Tabs.Content>
|
||||
))}
|
||||
</div>
|
||||
</Tabs>
|
||||
@@ -120,7 +119,6 @@ function AnalyticsPage({ params }: Route.ComponentProps) {
|
||||
label: "Create a project",
|
||||
onClick: () => {
|
||||
toggleCreateProjectModal(true);
|
||||
captureClick({ elementName: PROJECT_TRACKER_ELEMENTS.EMPTY_STATE_CREATE_PROJECT_BUTTON });
|
||||
},
|
||||
disabled: !canPerformEmptyStateActions,
|
||||
},
|
||||
|
||||
+2
-22
@@ -2,7 +2,7 @@ import { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams, useRouter, useSearchParams } from "next/navigation";
|
||||
// constants
|
||||
import { EPageAccess, PROJECT_PAGE_TRACKER_EVENTS, PROJECT_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { EPageAccess } from "@plane/constants";
|
||||
// plane types
|
||||
import { Button } from "@plane/propel/button";
|
||||
import { PageIcon } from "@plane/propel/icons";
|
||||
@@ -12,7 +12,6 @@ import type { TPage } from "@plane/types";
|
||||
import { Breadcrumbs, Header } from "@plane/ui";
|
||||
// helpers
|
||||
import { BreadcrumbLink } from "@/components/common/breadcrumb-link";
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
// hooks
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
// plane web imports
|
||||
@@ -40,23 +39,10 @@ export const PagesListHeader = observer(function PagesListHeader() {
|
||||
|
||||
await createPage(payload)
|
||||
.then((res) => {
|
||||
captureSuccess({
|
||||
eventName: PROJECT_PAGE_TRACKER_EVENTS.create,
|
||||
payload: {
|
||||
id: res?.id,
|
||||
state: "SUCCESS",
|
||||
},
|
||||
});
|
||||
const pageId = `/${workspaceSlug}/projects/${currentProjectDetails?.id}/pages/${res?.id}`;
|
||||
router.push(pageId);
|
||||
})
|
||||
.catch((err) => {
|
||||
captureError({
|
||||
eventName: PROJECT_PAGE_TRACKER_EVENTS.create,
|
||||
payload: {
|
||||
state: "ERROR",
|
||||
},
|
||||
});
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
@@ -86,13 +72,7 @@ export const PagesListHeader = observer(function PagesListHeader() {
|
||||
</Header.LeftItem>
|
||||
{canCurrentUserCreatePage && (
|
||||
<Header.RightItem>
|
||||
<Button
|
||||
variant="primary"
|
||||
size="lg"
|
||||
onClick={handleCreatePage}
|
||||
loading={isCreatingPage}
|
||||
data-ph-element={PROJECT_TRACKER_ELEMENTS.CREATE_HEADER_BUTTON}
|
||||
>
|
||||
<Button variant="primary" size="lg" onClick={handleCreatePage} loading={isCreatingPage}>
|
||||
{isCreatingPage ? "Adding" : "Add page"}
|
||||
</Button>
|
||||
</Header.RightItem>
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import { useTheme } from "next-themes";
|
||||
// plane imports
|
||||
import { HEADER_GITHUB_ICON, GITHUB_REDIRECTED_TRACKER_EVENT } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
// assets
|
||||
import githubBlackImage from "@/app/assets/logos/github-black.png?url";
|
||||
import githubWhiteImage from "@/app/assets/logos/github-white.png?url";
|
||||
// helpers
|
||||
import { captureElementAndEvent } from "@/helpers/event-tracker.helper";
|
||||
|
||||
export function StarUsOnGitHubLink() {
|
||||
// plane hooks
|
||||
@@ -18,17 +15,6 @@ export function StarUsOnGitHubLink() {
|
||||
return (
|
||||
<a
|
||||
aria-label={t("home.star_us_on_github")}
|
||||
onClick={() =>
|
||||
captureElementAndEvent({
|
||||
element: {
|
||||
elementName: HEADER_GITHUB_ICON,
|
||||
},
|
||||
event: {
|
||||
eventName: GITHUB_REDIRECTED_TRACKER_EVENT,
|
||||
state: "SUCCESS",
|
||||
},
|
||||
})
|
||||
}
|
||||
className="flex flex-shrink-0 items-center gap-1.5 rounded-sm bg-layer-2 px-3 py-1.5"
|
||||
href="https://github.com/makeplane/plane"
|
||||
target="_blank"
|
||||
|
||||
@@ -8,8 +8,6 @@ import { SettingsMobileNav } from "@/components/settings/mobile";
|
||||
// plane imports
|
||||
import { WORKSPACE_SETTINGS_ACCESS } from "@plane/constants";
|
||||
import type { EUserWorkspaceRoles } from "@plane/types";
|
||||
// plane web components
|
||||
import { SettingsRightSidebar } from "@/plane-web/components/settings/right-sidebar";
|
||||
// hooks
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
// local components
|
||||
@@ -48,7 +46,6 @@ const WorkspaceSettingLayout = observer(function WorkspaceSettingLayout({ params
|
||||
<div className="w-full h-full overflow-y-scroll md:pt-page-y">
|
||||
<Outlet />
|
||||
</div>
|
||||
<SettingsRightSidebar workspaceSlug={workspaceSlug} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
+2
-29
@@ -1,12 +1,7 @@
|
||||
import { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// types
|
||||
import {
|
||||
EUserPermissions,
|
||||
EUserPermissionsLevel,
|
||||
MEMBER_TRACKER_ELEMENTS,
|
||||
MEMBER_TRACKER_EVENTS,
|
||||
} from "@plane/constants";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { Button } from "@plane/propel/button";
|
||||
import { SearchIcon } from "@plane/propel/icons";
|
||||
@@ -20,8 +15,6 @@ import { PageHead } from "@/components/core/page-title";
|
||||
import { MemberListFiltersDropdown } from "@/components/project/dropdowns/filters/member-list";
|
||||
import { SettingsContentWrapper } from "@/components/settings/content-wrapper";
|
||||
import { WorkspaceMembersList } from "@/components/workspace/settings/members-list";
|
||||
// helpers
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
// hooks
|
||||
import { useMember } from "@/hooks/store/use-member";
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
@@ -58,13 +51,6 @@ const WorkspaceMembersSettingsPage = observer(function WorkspaceMembersSettingsP
|
||||
|
||||
setInviteModal(false);
|
||||
|
||||
captureSuccess({
|
||||
eventName: MEMBER_TRACKER_EVENTS.invite,
|
||||
payload: {
|
||||
emails: data.emails.map((email) => email.email),
|
||||
},
|
||||
});
|
||||
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: "Success!",
|
||||
@@ -76,14 +62,6 @@ const WorkspaceMembersSettingsPage = observer(function WorkspaceMembersSettingsP
|
||||
const err = error as Error & { error?: string };
|
||||
message = err.error;
|
||||
}
|
||||
captureError({
|
||||
eventName: MEMBER_TRACKER_EVENTS.invite,
|
||||
payload: {
|
||||
emails: data.emails.map((email) => email.email),
|
||||
},
|
||||
error: error as Error,
|
||||
});
|
||||
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
@@ -153,12 +131,7 @@ const WorkspaceMembersSettingsPage = observer(function WorkspaceMembersSettingsP
|
||||
/>
|
||||
<MembersActivityButton workspaceSlug={workspaceSlug} />
|
||||
{canPerformWorkspaceAdminActions && (
|
||||
<Button
|
||||
variant="primary"
|
||||
size="lg"
|
||||
onClick={() => setInviteModal(true)}
|
||||
data-ph-element={MEMBER_TRACKER_ELEMENTS.HEADER_ADD_BUTTON}
|
||||
>
|
||||
<Button variant="primary" size="lg" onClick={() => setInviteModal(true)}>
|
||||
{t("workspace_settings.settings.members.add_member")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
+1
-14
@@ -1,7 +1,7 @@
|
||||
import { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import useSWR from "swr";
|
||||
import { EUserPermissions, EUserPermissionsLevel, WORKSPACE_SETTINGS_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import type { IWebhook } from "@plane/types";
|
||||
// ui
|
||||
@@ -11,7 +11,6 @@ import { PageHead } from "@/components/core/page-title";
|
||||
import { SettingsContentWrapper } from "@/components/settings/content-wrapper";
|
||||
import { DeleteWebhookModal, WebhookDeleteSection, WebhookForm } from "@/components/web-hooks";
|
||||
// hooks
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { useWebhook } from "@/hooks/store/use-webhook";
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
@@ -55,12 +54,6 @@ function WebhookDetailsPage({ params }: Route.ComponentProps) {
|
||||
|
||||
try {
|
||||
await updateWebhook(workspaceSlug, formData.id, payload);
|
||||
|
||||
captureSuccess({
|
||||
eventName: WORKSPACE_SETTINGS_TRACKER_EVENTS.webhook_updated,
|
||||
payload: { webhook: formData.id },
|
||||
});
|
||||
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: "Success!",
|
||||
@@ -68,12 +61,6 @@ function WebhookDetailsPage({ params }: Route.ComponentProps) {
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
captureError({
|
||||
eventName: WORKSPACE_SETTINGS_TRACKER_EVENTS.webhook_updated,
|
||||
payload: { webhook: formData.id },
|
||||
error: error as Error,
|
||||
});
|
||||
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
|
||||
+1
-8
@@ -2,7 +2,7 @@ import { useEffect, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import useSWR from "swr";
|
||||
// plane imports
|
||||
import { EUserPermissions, EUserPermissionsLevel, WORKSPACE_SETTINGS_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
// components
|
||||
import { EmptyStateCompact } from "@plane/propel/empty-state";
|
||||
@@ -13,7 +13,6 @@ import { SettingsHeading } from "@/components/settings/heading";
|
||||
import { WebhookSettingsLoader } from "@/components/ui/loader/settings/web-hook";
|
||||
import { WebhooksList, CreateWebhookModal } from "@/components/web-hooks";
|
||||
// hooks
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
import { useWebhook } from "@/hooks/store/use-webhook";
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
@@ -72,9 +71,6 @@ function WebhooksListPage({ params }: Route.ComponentProps) {
|
||||
button={{
|
||||
label: t("workspace_settings.settings.webhooks.add_webhook"),
|
||||
onClick: () => {
|
||||
captureClick({
|
||||
elementName: WORKSPACE_SETTINGS_TRACKER_ELEMENTS.HEADER_ADD_WEBHOOK_BUTTON,
|
||||
});
|
||||
setShowCreateWebhookModal(true);
|
||||
},
|
||||
}}
|
||||
@@ -94,9 +90,6 @@ function WebhooksListPage({ params }: Route.ComponentProps) {
|
||||
{
|
||||
label: t("settings_empty_state.webhooks.cta_primary"),
|
||||
onClick: () => {
|
||||
captureClick({
|
||||
elementName: WORKSPACE_SETTINGS_TRACKER_ELEMENTS.EMPTY_STATE_ADD_WEBHOOK_BUTTON,
|
||||
});
|
||||
setShowCreateWebhookModal(true);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React, { useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import useSWR from "swr";
|
||||
// plane imports
|
||||
import { PROFILE_SETTINGS_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
// component
|
||||
import { EmptyStateCompact } from "@plane/propel/empty-state";
|
||||
@@ -14,7 +13,6 @@ import { SettingsHeading } from "@/components/settings/heading";
|
||||
import { APITokenSettingsLoader } from "@/components/ui/loader/settings/api-token";
|
||||
import { API_TOKENS_LIST } from "@/constants/fetch-keys";
|
||||
// store hooks
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
|
||||
const apiTokenService = new APITokenService();
|
||||
@@ -51,9 +49,6 @@ function ApiTokensPage() {
|
||||
button={{
|
||||
label: t("workspace_settings.settings.api_tokens.add_token"),
|
||||
onClick: () => {
|
||||
captureClick({
|
||||
elementName: PROFILE_SETTINGS_TRACKER_ELEMENTS.HEADER_ADD_PAT_BUTTON,
|
||||
});
|
||||
setIsCreateTokenModalOpen(true);
|
||||
},
|
||||
}}
|
||||
@@ -72,9 +67,6 @@ function ApiTokensPage() {
|
||||
button={{
|
||||
label: t("workspace_settings.settings.api_tokens.add_token"),
|
||||
onClick: () => {
|
||||
captureClick({
|
||||
elementName: PROFILE_SETTINGS_TRACKER_ELEMENTS.HEADER_ADD_PAT_BUTTON,
|
||||
});
|
||||
setIsCreateTokenModalOpen(true);
|
||||
},
|
||||
}}
|
||||
@@ -89,9 +81,6 @@ function ApiTokensPage() {
|
||||
{
|
||||
label: t("settings_empty_state.tokens.cta_primary"),
|
||||
onClick: () => {
|
||||
captureClick({
|
||||
elementName: PROFILE_SETTINGS_TRACKER_ELEMENTS.EMPTY_STATE_ADD_PAT_BUTTON,
|
||||
});
|
||||
setIsCreateTokenModalOpen(true);
|
||||
},
|
||||
},
|
||||
|
||||
+3
-7
@@ -7,13 +7,10 @@ import { SettingsMobileNav } from "@/components/settings/mobile";
|
||||
import { ProjectSettingsSidebar } from "@/components/settings/project/sidebar";
|
||||
// plane web imports
|
||||
import { ProjectAuthWrapper } from "@/plane-web/layouts/project-wrapper";
|
||||
import { SettingsRightSidebar } from "@/plane-web/components/settings/right-sidebar";
|
||||
// types
|
||||
import type { Route } from "./+types/layout";
|
||||
|
||||
export const ProjectDetailSettingsLayout = observer(function ProjectDetailSettingsLayout({
|
||||
params,
|
||||
}: Route.ComponentProps) {
|
||||
function ProjectDetailSettingsLayout({ params }: Route.ComponentProps) {
|
||||
const { workspaceSlug, projectId } = params;
|
||||
// router
|
||||
const pathname = usePathname();
|
||||
@@ -27,11 +24,10 @@ export const ProjectDetailSettingsLayout = observer(function ProjectDetailSettin
|
||||
<div className="w-full h-full overflow-y-scroll md:pt-page-y">
|
||||
<Outlet />
|
||||
</div>
|
||||
<SettingsRightSidebar workspaceSlug={workspaceSlug} projectId={projectId} />
|
||||
</ProjectAuthWrapper>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export default ProjectDetailSettingsLayout;
|
||||
export default observer(ProjectDetailSettingsLayout);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import Link from "next/link";
|
||||
|
||||
import useSWR, { mutate } from "swr";
|
||||
import { CheckCircle2 } from "lucide-react";
|
||||
// plane imports
|
||||
import { ROLE, MEMBER_TRACKER_EVENTS, MEMBER_TRACKER_ELEMENTS, GROUP_WORKSPACE_TRACKER_EVENT } from "@plane/constants";
|
||||
import { ROLE } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
// types
|
||||
import { Button } from "@plane/propel/button";
|
||||
@@ -19,9 +19,7 @@ import emptyInvitation from "@/app/assets/empty-state/invitation.svg?url";
|
||||
import { EmptyState } from "@/components/common/empty-state";
|
||||
import { WorkspaceLogo } from "@/components/workspace/logo";
|
||||
import { USER_WORKSPACES_LIST } from "@/constants/fetch-keys";
|
||||
// helpers
|
||||
// hooks
|
||||
import { captureError, captureSuccess, joinEventGroup } from "@/helpers/event-tracker.helper";
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
import { useUser, useUserProfile } from "@/hooks/store/user";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
@@ -79,18 +77,6 @@ function UserInvitationsPage() {
|
||||
const firstInviteId = invitationsRespond[0];
|
||||
const invitation = invitations?.find((i) => i.id === firstInviteId);
|
||||
const redirectWorkspace = invitations?.find((i) => i.id === firstInviteId)?.workspace;
|
||||
if (redirectWorkspace?.id) {
|
||||
joinEventGroup(GROUP_WORKSPACE_TRACKER_EVENT, redirectWorkspace?.id, {
|
||||
date: new Date().toDateString(),
|
||||
workspace_id: redirectWorkspace?.id,
|
||||
});
|
||||
}
|
||||
captureSuccess({
|
||||
eventName: MEMBER_TRACKER_EVENTS.accept,
|
||||
payload: {
|
||||
member_id: invitation?.id,
|
||||
},
|
||||
});
|
||||
updateUserProfile({ last_workspace_id: redirectWorkspace?.id })
|
||||
.then(() => {
|
||||
setIsJoiningWorkspaces(false);
|
||||
@@ -107,14 +93,7 @@ function UserInvitationsPage() {
|
||||
setIsJoiningWorkspaces(false);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
captureError({
|
||||
eventName: MEMBER_TRACKER_EVENTS.accept,
|
||||
payload: {
|
||||
member_id: invitationsRespond?.[0],
|
||||
},
|
||||
error: err,
|
||||
});
|
||||
.catch((_err) => {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: t("error"),
|
||||
@@ -183,7 +162,6 @@ function UserInvitationsPage() {
|
||||
onClick={submitInvitations}
|
||||
disabled={isJoiningWorkspaces || invitationsRespond.length === 0}
|
||||
loading={isJoiningWorkspaces}
|
||||
data-ph-element={MEMBER_TRACKER_ELEMENTS.ACCEPT_INVITATION_BUTTON}
|
||||
>
|
||||
{t("accept_and_join")}
|
||||
</Button>
|
||||
|
||||
@@ -29,10 +29,6 @@ const ChatSupportModal = lazy(function ChatSupportModal() {
|
||||
return import("@/components/global/chat-support-modal");
|
||||
});
|
||||
|
||||
const PostHogProvider = lazy(function PostHogProvider() {
|
||||
return import("@/lib/posthog-provider");
|
||||
});
|
||||
|
||||
export interface IAppProvider {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
@@ -52,9 +48,7 @@ export function AppProvider(props: IAppProvider) {
|
||||
<InstanceWrapper>
|
||||
<Suspense>
|
||||
<ChatSupportModal />
|
||||
<PostHogProvider>
|
||||
<SWRConfig value={WEB_SWR_CONFIG}>{children}</SWRConfig>
|
||||
</PostHogProvider>
|
||||
<SWRConfig value={WEB_SWR_CONFIG}>{children}</SWRConfig>
|
||||
</Suspense>
|
||||
</InstanceWrapper>
|
||||
</StoreWrapper>
|
||||
|
||||
@@ -10,8 +10,6 @@ import IssuesTour from "@/app/assets/onboarding/issues.webp?url";
|
||||
import ModulesTour from "@/app/assets/onboarding/modules.webp?url";
|
||||
import PagesTour from "@/app/assets/onboarding/pages.webp?url";
|
||||
import ViewsTour from "@/app/assets/onboarding/views.webp?url";
|
||||
// helpers
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
// hooks
|
||||
import { useCommandPalette } from "@/hooks/store/use-command-palette";
|
||||
import { useUser } from "@/hooks/store/user";
|
||||
@@ -107,9 +105,6 @@ export const TourRoot = observer(function TourRoot(props: TOnboardingTourProps)
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
captureClick({
|
||||
elementName: PRODUCT_TOUR_TRACKER_ELEMENTS.START_BUTTON,
|
||||
});
|
||||
setStep("work-items");
|
||||
}}
|
||||
>
|
||||
@@ -119,9 +114,6 @@ export const TourRoot = observer(function TourRoot(props: TOnboardingTourProps)
|
||||
type="button"
|
||||
className="bg-transparent text-11 font-medium text-accent-primary outline-subtle-1"
|
||||
onClick={() => {
|
||||
captureClick({
|
||||
elementName: PRODUCT_TOUR_TRACKER_ELEMENTS.SKIP_BUTTON,
|
||||
});
|
||||
onComplete();
|
||||
}}
|
||||
>
|
||||
@@ -170,9 +162,6 @@ export const TourRoot = observer(function TourRoot(props: TOnboardingTourProps)
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
captureClick({
|
||||
elementName: PRODUCT_TOUR_TRACKER_ELEMENTS.CREATE_PROJECT_BUTTON,
|
||||
});
|
||||
onComplete();
|
||||
toggleCreateProjectModal(true);
|
||||
}}
|
||||
|
||||
@@ -2,10 +2,7 @@ import { useState, useEffect, useRef } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { LockKeyhole, LockKeyholeOpen } from "lucide-react";
|
||||
// plane imports
|
||||
import { PROJECT_PAGE_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
// helpers
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
// hooks
|
||||
import { usePageOperations } from "@/hooks/use-page-operations";
|
||||
// store
|
||||
@@ -79,7 +76,6 @@ export const PageLockControl = observer(function PageLockControl({ page }: Props
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleLock}
|
||||
data-ph-element={PROJECT_PAGE_TRACKER_ELEMENTS.LOCK_BUTTON}
|
||||
className="flex-shrink-0 size-6 grid place-items-center rounded-sm text-secondary hover:text-primary hover:bg-layer-1 transition-colors"
|
||||
aria-label="Lock"
|
||||
>
|
||||
@@ -92,7 +88,6 @@ export const PageLockControl = observer(function PageLockControl({ page }: Props
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleLock}
|
||||
data-ph-element={PROJECT_PAGE_TRACKER_ELEMENTS.LOCK_BUTTON}
|
||||
className="h-6 flex items-center gap-1 px-2 rounded-sm text-accent-primary bg-accent-primary/20 hover:bg-accent-primary/30 transition-colors"
|
||||
aria-label="Locked"
|
||||
>
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import { PROJECT_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { observer } from "mobx-react";
|
||||
import { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { FormProvider, useForm } from "react-hook-form";
|
||||
// ui
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import { EFileAssetType } from "@plane/types";
|
||||
// constants
|
||||
// components
|
||||
import ProjectCommonAttributes from "@/components/project/create/common-attributes";
|
||||
import ProjectCreateHeader from "@/components/project/create/header";
|
||||
import ProjectCreateButtons from "@/components/project/create/project-create-buttons";
|
||||
// hooks
|
||||
import { getCoverImageType, uploadCoverImage } from "@/helpers/cover-image.helper";
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// plane web types
|
||||
@@ -97,12 +95,6 @@ export const CreateProjectForm = observer(function CreateProjectForm(props: TCre
|
||||
await updateCoverImageStatus(res.id, coverImage);
|
||||
await updateProject(workspaceSlug.toString(), res.id, { cover_image_url: coverImage });
|
||||
}
|
||||
captureSuccess({
|
||||
eventName: PROJECT_TRACKER_EVENTS.create,
|
||||
payload: {
|
||||
identifier: formData.identifier,
|
||||
},
|
||||
});
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: t("success"),
|
||||
@@ -116,13 +108,6 @@ export const CreateProjectForm = observer(function CreateProjectForm(props: TCre
|
||||
})
|
||||
.catch((err) => {
|
||||
try {
|
||||
captureError({
|
||||
eventName: PROJECT_TRACKER_EVENTS.create,
|
||||
payload: {
|
||||
identifier: formData.identifier,
|
||||
},
|
||||
});
|
||||
|
||||
// Handle the new error format where codes are nested in arrays under field names
|
||||
const errorData = err?.data ?? {};
|
||||
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
type TProjectMembersActivityButtonProps = { workspaceSlug: string; projectId: string };
|
||||
|
||||
export const ProjectMembersActivityButton = observer(function ProjectMembersActivityButton(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
props: TProjectMembersActivityButtonProps
|
||||
) {
|
||||
return <></>;
|
||||
});
|
||||
@@ -1,10 +0,0 @@
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
type TSettingsRightSidebarProps = { workspaceSlug: string; projectId?: string };
|
||||
|
||||
export const SettingsRightSidebar = observer(function SettingsRightSidebar(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
props: TSettingsRightSidebarProps
|
||||
) {
|
||||
return <></>;
|
||||
});
|
||||
@@ -4,8 +4,6 @@ import {
|
||||
SUBSCRIPTION_REDIRECTION_URLS,
|
||||
SUBSCRIPTION_WITH_BILLING_FREQUENCY,
|
||||
TALK_TO_SALES_URL,
|
||||
WORKSPACE_SETTINGS_TRACKER_ELEMENTS,
|
||||
WORKSPACE_SETTINGS_TRACKER_EVENTS,
|
||||
} from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { Button } from "@plane/propel/button";
|
||||
@@ -16,7 +14,6 @@ import { getSubscriptionName } from "@plane/utils";
|
||||
import { DiscountInfo } from "@/components/license/modal/card/discount-info";
|
||||
import type { TPlanDetail } from "@/constants/plans";
|
||||
// local imports
|
||||
import { captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { PlanFrequencyToggle } from "./frequency-toggle";
|
||||
|
||||
type TPlanDetailProps = {
|
||||
@@ -45,12 +42,6 @@ export const PlanDetail = observer(function PlanDetail(props: TPlanDetailProps)
|
||||
const frequency = billingFrequency ?? "year";
|
||||
// Get the redirection URL based on the subscription type and billing frequency
|
||||
const redirectUrl = SUBSCRIPTION_REDIRECTION_URLS[subscriptionType][frequency] ?? TALK_TO_SALES_URL;
|
||||
captureSuccess({
|
||||
eventName: WORKSPACE_SETTINGS_TRACKER_EVENTS.upgrade_plan_redirected,
|
||||
payload: {
|
||||
subscriptionType,
|
||||
},
|
||||
});
|
||||
// Open the URL in a new tab
|
||||
window.open(redirectUrl, "_blank");
|
||||
};
|
||||
@@ -103,17 +94,7 @@ export const PlanDetail = observer(function PlanDetail(props: TPlanDetailProps)
|
||||
|
||||
{/* Subscription button */}
|
||||
<div className="flex flex-col gap-1 py-3 items-start">
|
||||
<Button
|
||||
variant="primary"
|
||||
size="lg"
|
||||
onClick={handleRedirection}
|
||||
className="w-full"
|
||||
data-ph-element={
|
||||
isSubscriptionActive
|
||||
? WORKSPACE_SETTINGS_TRACKER_ELEMENTS.BILLING_UPGRADE_BUTTON(subscriptionType)
|
||||
: WORKSPACE_SETTINGS_TRACKER_ELEMENTS.BILLING_TALK_TO_SALES_BUTTON
|
||||
}
|
||||
>
|
||||
<Button variant="primary" size="lg" onClick={handleRedirection} className="w-full">
|
||||
{isSubscriptionActive ? `Upgrade to ${subscriptionName}` : t("common.upgrade_cta.talk_to_sales")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
// types
|
||||
import {
|
||||
CYCLE_TRACKER_ELEMENTS,
|
||||
MODULE_TRACKER_ELEMENTS,
|
||||
PROJECT_PAGE_TRACKER_ELEMENTS,
|
||||
PROJECT_TRACKER_ELEMENTS,
|
||||
PROJECT_VIEW_TRACKER_ELEMENTS,
|
||||
WORK_ITEM_TRACKER_ELEMENTS,
|
||||
} from "@plane/constants";
|
||||
import type { TCommandPaletteActionList, TCommandPaletteShortcut, TCommandPaletteShortcutList } from "@plane/types";
|
||||
// store
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
import { store } from "@/lib/store-context";
|
||||
|
||||
export const getGlobalShortcutsList: () => TCommandPaletteActionList = () => {
|
||||
@@ -21,7 +11,6 @@ export const getGlobalShortcutsList: () => TCommandPaletteActionList = () => {
|
||||
description: "Create a new work item in the current project",
|
||||
action: () => {
|
||||
toggleCreateIssueModal(true);
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.COMMAND_PALETTE_ADD_BUTTON });
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -36,7 +25,6 @@ export const getWorkspaceShortcutsList: () => TCommandPaletteActionList = () =>
|
||||
description: "Create a new project in the current workspace",
|
||||
action: () => {
|
||||
toggleCreateProjectModal(true);
|
||||
captureClick({ elementName: PROJECT_TRACKER_ELEMENTS.COMMAND_PALETTE_SHORTCUT_CREATE_BUTTON });
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -57,7 +45,6 @@ export const getProjectShortcutsList: () => TCommandPaletteActionList = () => {
|
||||
description: "Create a new page in the current project",
|
||||
action: () => {
|
||||
toggleCreatePageModal({ isOpen: true });
|
||||
captureClick({ elementName: PROJECT_PAGE_TRACKER_ELEMENTS.COMMAND_PALETTE_SHORTCUT_CREATE_BUTTON });
|
||||
},
|
||||
},
|
||||
m: {
|
||||
@@ -65,7 +52,6 @@ export const getProjectShortcutsList: () => TCommandPaletteActionList = () => {
|
||||
description: "Create a new module in the current project",
|
||||
action: () => {
|
||||
toggleCreateModuleModal(true);
|
||||
captureClick({ elementName: MODULE_TRACKER_ELEMENTS.COMMAND_PALETTE_ADD_ITEM });
|
||||
},
|
||||
},
|
||||
q: {
|
||||
@@ -73,7 +59,6 @@ export const getProjectShortcutsList: () => TCommandPaletteActionList = () => {
|
||||
description: "Create a new cycle in the current project",
|
||||
action: () => {
|
||||
toggleCreateCycleModal(true);
|
||||
captureClick({ elementName: CYCLE_TRACKER_ELEMENTS.COMMAND_PALETTE_ADD_ITEM });
|
||||
},
|
||||
},
|
||||
v: {
|
||||
@@ -81,7 +66,6 @@ export const getProjectShortcutsList: () => TCommandPaletteActionList = () => {
|
||||
description: "Create a new view in the current project",
|
||||
action: () => {
|
||||
toggleCreateViewModal(true);
|
||||
captureClick({ elementName: PROJECT_VIEW_TRACKER_ELEMENTS.COMMAND_PALETTE_ADD_ITEM });
|
||||
},
|
||||
},
|
||||
backspace: {
|
||||
|
||||
@@ -41,13 +41,4 @@ export class ProjectMemberStore extends BaseProjectMemberStore implements IProje
|
||||
* @param userId - The ID of the user to remove from the project
|
||||
*/
|
||||
processMemberRemoval = (projectId: string, userId: string) => this.handleMemberRemoval(projectId, userId);
|
||||
|
||||
/**
|
||||
* @description Mutate project members activity
|
||||
* @param workspaceSlug
|
||||
* @param projectId
|
||||
*/
|
||||
mutateProjectMembersActivity = async (_workspaceSlug: string, _projectId: string) => {
|
||||
// No-op in default/CE version
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,14 +5,11 @@ import { Controller, useForm } from "react-hook-form";
|
||||
// icons
|
||||
import { CircleCheck } from "lucide-react";
|
||||
// plane imports
|
||||
import { AUTH_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { Button, getButtonStyling } from "@plane/propel/button";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import { Input } from "@plane/ui";
|
||||
import { cn, checkEmailValidity } from "@plane/utils";
|
||||
// helpers
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
// hooks
|
||||
import useTimer from "@/hooks/use-timer";
|
||||
// services
|
||||
@@ -59,12 +56,6 @@ export const ForgotPasswordForm = observer(function ForgotPasswordForm() {
|
||||
email: formData.email,
|
||||
})
|
||||
.then(() => {
|
||||
captureSuccess({
|
||||
eventName: AUTH_TRACKER_EVENTS.forgot_password,
|
||||
payload: {
|
||||
email: formData.email,
|
||||
},
|
||||
});
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: t("auth.forgot_password.toast.success.title"),
|
||||
@@ -73,12 +64,6 @@ export const ForgotPasswordForm = observer(function ForgotPasswordForm() {
|
||||
setResendCodeTimer(30);
|
||||
})
|
||||
.catch((err) => {
|
||||
captureError({
|
||||
eventName: AUTH_TRACKER_EVENTS.forgot_password,
|
||||
payload: {
|
||||
email: formData.email,
|
||||
},
|
||||
});
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: t("auth.forgot_password.toast.error.title"),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import Link from "next/link";
|
||||
// icons
|
||||
@@ -15,8 +15,6 @@ import { ForgotPasswordPopover } from "@/components/account/auth-forms/forgot-pa
|
||||
// constants
|
||||
// helpers
|
||||
import { EAuthModes, EAuthSteps } from "@/helpers/authentication.helper";
|
||||
// hooks
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
// services
|
||||
import { AuthService } from "@/services/auth.service";
|
||||
|
||||
@@ -154,15 +152,6 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props)
|
||||
: true;
|
||||
if (isPasswordValid) {
|
||||
setIsSubmitting(true);
|
||||
captureSuccess({
|
||||
eventName:
|
||||
mode === EAuthModes.SIGN_IN
|
||||
? AUTH_TRACKER_EVENTS.sign_in_with_password
|
||||
: AUTH_TRACKER_EVENTS.sign_up_with_password,
|
||||
payload: {
|
||||
email: passwordFormData.email,
|
||||
},
|
||||
});
|
||||
if (formRef.current) formRef.current.submit(); // Manually submit the form if the condition is met
|
||||
} else {
|
||||
setBannerMessage(true);
|
||||
@@ -170,15 +159,6 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props)
|
||||
}}
|
||||
onError={() => {
|
||||
setIsSubmitting(false);
|
||||
captureError({
|
||||
eventName:
|
||||
mode === EAuthModes.SIGN_IN
|
||||
? AUTH_TRACKER_EVENTS.sign_in_with_password
|
||||
: AUTH_TRACKER_EVENTS.sign_up_with_password,
|
||||
payload: {
|
||||
email: passwordFormData.email,
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<input type="hidden" name="csrfmiddlewaretoken" />
|
||||
|
||||
@@ -5,15 +5,13 @@ import { useSearchParams } from "next/navigation";
|
||||
// icons
|
||||
import { Eye, EyeOff } from "lucide-react";
|
||||
// plane imports
|
||||
import { AUTH_TRACKER_ELEMENTS, AUTH_TRACKER_EVENTS, E_PASSWORD_STRENGTH } from "@plane/constants";
|
||||
import { E_PASSWORD_STRENGTH } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { Button } from "@plane/propel/button";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import { Input, PasswordStrengthIndicator } from "@plane/ui";
|
||||
// components
|
||||
import { getPasswordStrength } from "@plane/utils";
|
||||
// helpers
|
||||
import { captureError, captureSuccess, captureView } from "@/helpers/event-tracker.helper";
|
||||
// hooks
|
||||
import { useUser } from "@/hooks/store/user";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
@@ -60,12 +58,6 @@ export const SetPasswordForm = observer(function SetPasswordForm() {
|
||||
// hooks
|
||||
const { data: user, handleSetPassword } = useUser();
|
||||
|
||||
useEffect(() => {
|
||||
captureView({
|
||||
elementName: AUTH_TRACKER_ELEMENTS.SET_PASSWORD_FORM,
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (csrfToken === undefined)
|
||||
authService.requestCSRFToken().then((data) => data?.csrf_token && setCsrfToken(data.csrf_token));
|
||||
@@ -92,9 +84,6 @@ export const SetPasswordForm = observer(function SetPasswordForm() {
|
||||
e.preventDefault();
|
||||
if (!csrfToken) throw new Error("csrf token not found");
|
||||
await handleSetPassword(csrfToken, { password: passwordFormData.password });
|
||||
captureSuccess({
|
||||
eventName: AUTH_TRACKER_EVENTS.password_created,
|
||||
});
|
||||
router.push("/");
|
||||
} catch (error: unknown) {
|
||||
let message = undefined;
|
||||
@@ -102,9 +91,6 @@ export const SetPasswordForm = observer(function SetPasswordForm() {
|
||||
const err = error as Error & { error?: string };
|
||||
message = err.error;
|
||||
}
|
||||
captureError({
|
||||
eventName: AUTH_TRACKER_EVENTS.password_created,
|
||||
});
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: t("common.errors.default.title"),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { CircleCheck, XCircle } from "lucide-react";
|
||||
import { API_BASE_URL, AUTH_TRACKER_ELEMENTS, AUTH_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { API_BASE_URL } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { Button } from "@plane/propel/button";
|
||||
import { Input, Spinner } from "@plane/ui";
|
||||
@@ -8,7 +8,6 @@ import { Input, Spinner } from "@plane/ui";
|
||||
// helpers
|
||||
import { EAuthModes } from "@/helpers/authentication.helper";
|
||||
// hooks
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import useTimer from "@/hooks/use-timer";
|
||||
// services
|
||||
import { AuthService } from "@/services/auth.service";
|
||||
@@ -59,22 +58,10 @@ export function AuthUniqueCodeForm(props: TAuthUniqueCodeForm) {
|
||||
setResendCodeTimer(defaultResetTimerValue);
|
||||
handleFormChange("code", uniqueCode?.code || "");
|
||||
setIsRequestingNewCode(false);
|
||||
captureSuccess({
|
||||
eventName: AUTH_TRACKER_EVENTS.new_code_requested,
|
||||
payload: {
|
||||
email: email,
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
setResendCodeTimer(0);
|
||||
console.error("Error while requesting new code");
|
||||
setIsRequestingNewCode(false);
|
||||
captureError({
|
||||
eventName: AUTH_TRACKER_EVENTS.new_code_requested,
|
||||
payload: {
|
||||
email: email,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -93,22 +80,9 @@ export function AuthUniqueCodeForm(props: TAuthUniqueCodeForm) {
|
||||
action={`${API_BASE_URL}/auth/${mode === EAuthModes.SIGN_IN ? "magic-sign-in" : "magic-sign-up"}/`}
|
||||
onSubmit={() => {
|
||||
setIsSubmitting(true);
|
||||
captureSuccess({
|
||||
eventName: AUTH_TRACKER_EVENTS.code_verify,
|
||||
payload: {
|
||||
state: "SUCCESS",
|
||||
first_time: !isExistingEmail,
|
||||
},
|
||||
});
|
||||
}}
|
||||
onError={() => {
|
||||
setIsSubmitting(false);
|
||||
captureError({
|
||||
eventName: AUTH_TRACKER_EVENTS.code_verify,
|
||||
payload: {
|
||||
state: "FAILED",
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
|
||||
@@ -163,7 +137,6 @@ export function AuthUniqueCodeForm(props: TAuthUniqueCodeForm) {
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
data-ph-element={AUTH_TRACKER_ELEMENTS.REQUEST_NEW_CODE}
|
||||
onClick={() => generateNewCode(uniqueCodeFormData.email)}
|
||||
className={
|
||||
isRequestNewCodeDisabled
|
||||
@@ -182,14 +155,7 @@ export function AuthUniqueCodeForm(props: TAuthUniqueCodeForm) {
|
||||
</div>
|
||||
|
||||
<div className="space-y-2.5">
|
||||
<Button
|
||||
type="submit"
|
||||
variant="primary"
|
||||
className="w-full"
|
||||
size="xl"
|
||||
disabled={isButtonDisabled}
|
||||
data-ph-element={AUTH_TRACKER_ELEMENTS.VERIFY_CODE}
|
||||
>
|
||||
<Button type="submit" variant="primary" className="w-full" size="xl" disabled={isButtonDisabled}>
|
||||
{isRequestingNewCode ? (
|
||||
t("auth.common.unique_code.sending_code")
|
||||
) : isSubmitting ? (
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { PROFILE_SETTINGS_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
// ui
|
||||
import { Button } from "@plane/propel/button";
|
||||
@@ -7,7 +6,6 @@ import { TrashIcon } from "@plane/propel/icons";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
|
||||
// hooks
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { useUser } from "@/hooks/store/user";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
|
||||
@@ -36,9 +34,6 @@ export function DeactivateAccountModal(props: Props) {
|
||||
|
||||
await deactivateAccount()
|
||||
.then(() => {
|
||||
captureSuccess({
|
||||
eventName: PROFILE_SETTINGS_TRACKER_EVENTS.deactivate_account,
|
||||
});
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: "Success!",
|
||||
@@ -50,9 +45,6 @@ export function DeactivateAccountModal(props: Props) {
|
||||
return;
|
||||
})
|
||||
.catch((err: any) => {
|
||||
captureError({
|
||||
eventName: PROFILE_SETTINGS_TRACKER_EVENTS.deactivate_account,
|
||||
});
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import type { FC } from "react";
|
||||
import { useState } from "react";
|
||||
import { mutate } from "swr";
|
||||
// types
|
||||
import { PROFILE_SETTINGS_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import { APITokenService } from "@plane/services";
|
||||
@@ -11,7 +9,6 @@ import type { IApiToken } from "@plane/types";
|
||||
import { AlertModalCore } from "@plane/ui";
|
||||
// fetch-keys
|
||||
import { API_TOKENS_LIST } from "@/constants/fetch-keys";
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
@@ -50,32 +47,18 @@ export function DeleteApiTokenModal(props: Props) {
|
||||
(prevData) => (prevData ?? []).filter((token) => token.id !== tokenId),
|
||||
false
|
||||
);
|
||||
captureSuccess({
|
||||
eventName: PROFILE_SETTINGS_TRACKER_EVENTS.pat_deleted,
|
||||
payload: {
|
||||
token: tokenId,
|
||||
},
|
||||
});
|
||||
|
||||
handleClose();
|
||||
setDeleteLoading(false);
|
||||
})
|
||||
.catch((err) =>
|
||||
.catch((err) => {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: t("workspace_settings.settings.api_tokens.delete.error.title"),
|
||||
message: err?.message ?? t("workspace_settings.settings.api_tokens.delete.error.message"),
|
||||
})
|
||||
)
|
||||
.catch((err) => {
|
||||
captureError({
|
||||
eventName: PROFILE_SETTINGS_TRACKER_EVENTS.pat_deleted,
|
||||
payload: {
|
||||
token: tokenId,
|
||||
},
|
||||
error: err as Error,
|
||||
});
|
||||
})
|
||||
.finally(() => setDeleteLoading(false));
|
||||
setDeleteLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { mutate } from "swr";
|
||||
// plane imports
|
||||
import { PROFILE_SETTINGS_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import { APITokenService } from "@plane/services";
|
||||
import type { IApiToken } from "@plane/types";
|
||||
@@ -9,8 +8,6 @@ import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
|
||||
import { renderFormattedDate, csvDownload } from "@plane/utils";
|
||||
// constants
|
||||
import { API_TOKENS_LIST } from "@/constants/fetch-keys";
|
||||
// helpers
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
// local imports
|
||||
import { CreateApiTokenForm } from "./form";
|
||||
import { GeneratedTokenDetails } from "./generated-token-details";
|
||||
@@ -66,12 +63,6 @@ export function CreateApiTokenModal(props: Props) {
|
||||
},
|
||||
false
|
||||
);
|
||||
captureSuccess({
|
||||
eventName: PROFILE_SETTINGS_TRACKER_EVENTS.pat_created,
|
||||
payload: {
|
||||
token: res.id,
|
||||
},
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
setToast({
|
||||
@@ -80,10 +71,6 @@ export function CreateApiTokenModal(props: Props) {
|
||||
message: err.message || err.detail,
|
||||
});
|
||||
|
||||
captureError({
|
||||
eventName: PROFILE_SETTINGS_TRACKER_EVENTS.pat_created,
|
||||
});
|
||||
|
||||
throw err;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,24 +1,16 @@
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { ArchiveRestore } from "lucide-react";
|
||||
// types
|
||||
import {
|
||||
PROJECT_AUTOMATION_MONTHS,
|
||||
EUserPermissions,
|
||||
EUserPermissionsLevel,
|
||||
PROJECT_SETTINGS_TRACKER_ELEMENTS,
|
||||
PROJECT_SETTINGS_TRACKER_EVENTS,
|
||||
} from "@plane/constants";
|
||||
import { PROJECT_AUTOMATION_MONTHS, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import type { IProject } from "@plane/types";
|
||||
// ui
|
||||
import { CustomSelect, Loader, ToggleSwitch } from "@plane/ui";
|
||||
// component
|
||||
import { SelectMonthModal } from "@/components/automation";
|
||||
// constants
|
||||
// hooks
|
||||
import { captureElementAndEvent } from "@/helpers/event-tracker.helper";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
|
||||
@@ -52,6 +44,14 @@ export const AutoArchiveAutomation = observer(function AutoArchiveAutomation(pro
|
||||
return currentProjectDetails.archive_in !== 0;
|
||||
}, [currentProjectDetails]);
|
||||
|
||||
const handleToggleArchive = async () => {
|
||||
if (currentProjectDetails?.archive_in === 0) {
|
||||
await handleChange({ archive_in: 1 });
|
||||
} else {
|
||||
await handleChange({ archive_in: 0 });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<SelectMonthModal
|
||||
@@ -74,27 +74,7 @@ export const AutoArchiveAutomation = observer(function AutoArchiveAutomation(pro
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<ToggleSwitch
|
||||
value={autoArchiveStatus}
|
||||
onChange={async () => {
|
||||
if (currentProjectDetails?.archive_in === 0) {
|
||||
await handleChange({ archive_in: 1 });
|
||||
} else {
|
||||
await handleChange({ archive_in: 0 });
|
||||
}
|
||||
captureElementAndEvent({
|
||||
element: {
|
||||
elementName: PROJECT_SETTINGS_TRACKER_ELEMENTS.AUTOMATIONS_ARCHIVE_TOGGLE_BUTTON,
|
||||
},
|
||||
event: {
|
||||
eventName: PROJECT_SETTINGS_TRACKER_EVENTS.auto_archive_workitems,
|
||||
state: "SUCCESS",
|
||||
},
|
||||
});
|
||||
}}
|
||||
size="sm"
|
||||
disabled={!isAdmin}
|
||||
/>
|
||||
<ToggleSwitch value={autoArchiveStatus} onChange={handleToggleArchive} size="sm" disabled={!isAdmin} />
|
||||
</div>
|
||||
|
||||
{currentProjectDetails ? (
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// icons
|
||||
import { ArchiveX } from "lucide-react";
|
||||
// types
|
||||
import {
|
||||
PROJECT_AUTOMATION_MONTHS,
|
||||
EUserPermissions,
|
||||
EUserPermissionsLevel,
|
||||
EIconSize,
|
||||
PROJECT_SETTINGS_TRACKER_ELEMENTS,
|
||||
PROJECT_SETTINGS_TRACKER_EVENTS,
|
||||
} from "@plane/constants";
|
||||
import { PROJECT_AUTOMATION_MONTHS, EUserPermissions, EUserPermissionsLevel, EIconSize } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { StateGroupIcon, StatePropertyIcon } from "@plane/propel/icons";
|
||||
import type { IProject } from "@plane/types";
|
||||
@@ -21,7 +14,6 @@ import { CustomSelect, CustomSearchSelect, ToggleSwitch, Loader } from "@plane/u
|
||||
import { SelectMonthModal } from "@/components/automation";
|
||||
// constants
|
||||
// hooks
|
||||
import { captureElementAndEvent } from "@/helpers/event-tracker.helper";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
import { useProjectState } from "@/hooks/store/use-project-state";
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
@@ -111,15 +103,6 @@ export const AutoCloseAutomation = observer(function AutoCloseAutomation(props:
|
||||
} else {
|
||||
await handleChange({ close_in: 0, default_state: null });
|
||||
}
|
||||
captureElementAndEvent({
|
||||
element: {
|
||||
elementName: PROJECT_SETTINGS_TRACKER_ELEMENTS.AUTOMATIONS_CLOSE_TOGGLE_BUTTON,
|
||||
},
|
||||
event: {
|
||||
eventName: PROJECT_SETTINGS_TRACKER_EVENTS.auto_close_workitems,
|
||||
state: "SUCCESS",
|
||||
},
|
||||
});
|
||||
}}
|
||||
size="sm"
|
||||
disabled={!isAdmin}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Popover } from "@headlessui/react";
|
||||
// plane imports
|
||||
import { ACCEPTED_COVER_IMAGE_MIME_TYPES_FOR_REACT_DROPZONE, MAX_FILE_SIZE } from "@plane/constants";
|
||||
import { useOutsideClickDetector } from "@plane/hooks";
|
||||
import { Tabs, TabsContent, TabsList, TabsIndicator, TabsTrigger } from "@plane/propel/tabs";
|
||||
import { Tabs } from "@plane/propel/tabs";
|
||||
import { Button, getButtonStyling } from "@plane/propel/button";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import { EFileAssetType } from "@plane/types";
|
||||
@@ -197,16 +197,16 @@ export const ImagePickerPopover = observer(function ImagePickerPopover(props: Pr
|
||||
className="flex h-96 w-80 flex-col overflow-auto rounded border border-subtle bg-surface-1 shadow-raised-200 md:h-[36rem] md:w-[36rem]"
|
||||
>
|
||||
<Tabs defaultValue={enabledTabs[0]?.key || "images"} className="flex h-full flex-col p-3">
|
||||
<TabsList className="flex rounded bg-layer-3 p-1">
|
||||
<Tabs.List className="flex rounded bg-layer-3 p-1">
|
||||
{enabledTabs.map((tab) => (
|
||||
<TabsTrigger key={tab.key} value={tab.key} size="md">
|
||||
<Tabs.Trigger key={tab.key} value={tab.key} size="md">
|
||||
{tab.title}
|
||||
</TabsTrigger>
|
||||
</Tabs.Trigger>
|
||||
))}
|
||||
<TabsIndicator />
|
||||
</TabsList>
|
||||
<Tabs.Indicator />
|
||||
</Tabs.List>
|
||||
<div className="vertical-scrollbar scrollbar-sm p-3 mt-3 flex-1 overflow-y-auto overflow-x-hidden">
|
||||
<TabsContent value="unsplash" className="h-full w-full space-y-4">
|
||||
<Tabs.Content value="unsplash" className="h-full w-full space-y-4">
|
||||
{(unsplashImages || !unsplashError) && (
|
||||
<>
|
||||
<div className="flex items-center gap-x-2">
|
||||
@@ -273,8 +273,8 @@ export const ImagePickerPopover = observer(function ImagePickerPopover(props: Pr
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</TabsContent>
|
||||
<TabsContent value="images" className="h-full w-full space-y-4">
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="images" className="h-full w-full space-y-4">
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
{Object.values(STATIC_COVER_IMAGES).map((imageUrl, index) => (
|
||||
<div
|
||||
@@ -290,8 +290,8 @@ export const ImagePickerPopover = observer(function ImagePickerPopover(props: Pr
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="upload" className="h-full w-full">
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="upload" className="h-full w-full">
|
||||
<div className="flex h-full w-full flex-col gap-y-2">
|
||||
<div className="flex w-full flex-1 items-center gap-3">
|
||||
<div
|
||||
@@ -358,7 +358,7 @@ export const ImagePickerPopover = observer(function ImagePickerPopover(props: Pr
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs.Content>
|
||||
</div>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
import type { FC } from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import { useEffect } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { ArrowRight } from "lucide-react";
|
||||
// Plane Imports
|
||||
import {
|
||||
CYCLE_TRACKER_EVENTS,
|
||||
CYCLE_STATUS,
|
||||
EUserPermissions,
|
||||
EUserPermissionsLevel,
|
||||
CYCLE_TRACKER_ELEMENTS,
|
||||
} from "@plane/constants";
|
||||
import { CYCLE_STATUS, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { ChevronRightIcon } from "@plane/propel/icons";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
@@ -19,7 +12,6 @@ import { getDate, renderFormattedPayloadDate } from "@plane/utils";
|
||||
// components
|
||||
import { DateRangeDropdown } from "@/components/dropdowns/date-range";
|
||||
// hooks
|
||||
import { captureElementAndEvent } from "@/helpers/event-tracker.helper";
|
||||
import { useCycle } from "@/hooks/store/use-cycle";
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
import { useTimeZoneConverter } from "@/hooks/use-timezone-converter";
|
||||
@@ -64,37 +56,7 @@ export const CycleSidebarHeader = observer(function CycleSidebarHeader(props: Pr
|
||||
|
||||
const submitChanges = async (data: Partial<ICycle>) => {
|
||||
if (!workspaceSlug || !projectId || !cycleDetails.id) return;
|
||||
|
||||
await updateCycleDetails(workspaceSlug.toString(), projectId.toString(), cycleDetails.id.toString(), data)
|
||||
.then(() => {
|
||||
captureElementAndEvent({
|
||||
element: {
|
||||
elementName: CYCLE_TRACKER_ELEMENTS.RIGHT_SIDEBAR,
|
||||
},
|
||||
event: {
|
||||
eventName: CYCLE_TRACKER_EVENTS.update,
|
||||
state: "SUCCESS",
|
||||
payload: {
|
||||
id: cycleDetails.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
.catch(() => {
|
||||
captureElementAndEvent({
|
||||
element: {
|
||||
elementName: CYCLE_TRACKER_ELEMENTS.RIGHT_SIDEBAR,
|
||||
},
|
||||
event: {
|
||||
eventName: CYCLE_TRACKER_EVENTS.update,
|
||||
state: "ERROR",
|
||||
payload: {
|
||||
id: cycleDetails.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
await updateCycleDetails(workspaceSlug.toString(), projectId.toString(), cycleDetails.id.toString(), data);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { useState } from "react";
|
||||
// ui
|
||||
import { CYCLE_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { Button } from "@plane/propel/button";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
|
||||
// hooks
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { useCycle } from "@/hooks/store/use-cycle";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
|
||||
@@ -43,12 +41,6 @@ export function ArchiveCycleModal(props: Props) {
|
||||
title: "Archive success",
|
||||
message: "Your archives can be found in project archives.",
|
||||
});
|
||||
captureSuccess({
|
||||
eventName: CYCLE_TRACKER_EVENTS.archive,
|
||||
payload: {
|
||||
id: cycleId,
|
||||
},
|
||||
});
|
||||
onClose();
|
||||
router.push(`/${workspaceSlug}/projects/${projectId}/cycles`);
|
||||
return;
|
||||
@@ -59,12 +51,6 @@ export function ArchiveCycleModal(props: Props) {
|
||||
title: "Error!",
|
||||
message: "Cycle could not be archived. Please try again.",
|
||||
});
|
||||
captureError({
|
||||
eventName: CYCLE_TRACKER_EVENTS.archive,
|
||||
payload: {
|
||||
id: cycleId,
|
||||
},
|
||||
});
|
||||
})
|
||||
.finally(() => setIsArchiving(false));
|
||||
};
|
||||
|
||||
@@ -2,14 +2,12 @@ import { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams, useSearchParams } from "next/navigation";
|
||||
// types
|
||||
import { PROJECT_ERROR_MESSAGES, CYCLE_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { PROJECT_ERROR_MESSAGES } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import type { ICycle } from "@plane/types";
|
||||
// ui
|
||||
import { AlertModalCore } from "@plane/ui";
|
||||
// helpers
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
// hooks
|
||||
import { useCycle } from "@/hooks/store/use-cycle";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
@@ -48,12 +46,6 @@ export const CycleDeleteModal = observer(function CycleDeleteModal(props: ICycle
|
||||
title: "Success!",
|
||||
message: "Cycle deleted successfully.",
|
||||
});
|
||||
captureSuccess({
|
||||
eventName: CYCLE_TRACKER_EVENTS.delete,
|
||||
payload: {
|
||||
id: cycle.id,
|
||||
},
|
||||
});
|
||||
})
|
||||
.catch((errors) => {
|
||||
const isPermissionError = errors?.error === "You don't have the required permissions.";
|
||||
@@ -65,13 +57,6 @@ export const CycleDeleteModal = observer(function CycleDeleteModal(props: ICycle
|
||||
type: TOAST_TYPE.ERROR,
|
||||
message: currentError.i18n_message && t(currentError.i18n_message),
|
||||
});
|
||||
captureError({
|
||||
eventName: CYCLE_TRACKER_EVENTS.delete,
|
||||
payload: {
|
||||
id: cycle.id,
|
||||
},
|
||||
error: errors,
|
||||
});
|
||||
})
|
||||
.finally(() => handleClose());
|
||||
} catch {
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import type { FC, MouseEvent } from "react";
|
||||
import type { MouseEvent } from "react";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Eye, ArrowRight, CalendarDays } from "lucide-react";
|
||||
// plane imports
|
||||
import {
|
||||
CYCLE_TRACKER_EVENTS,
|
||||
EUserPermissions,
|
||||
EUserPermissionsLevel,
|
||||
IS_FAVORITE_MENU_OPEN,
|
||||
CYCLE_TRACKER_ELEMENTS,
|
||||
} from "@plane/constants";
|
||||
import { EUserPermissions, EUserPermissionsLevel, IS_FAVORITE_MENU_OPEN } from "@plane/constants";
|
||||
import { useLocalStorage } from "@plane/hooks";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { TransferIcon, WorkItemsIcon, MembersPropertyIcon } from "@plane/propel/icons";
|
||||
@@ -25,7 +19,6 @@ import { DateRangeDropdown } from "@/components/dropdowns/date-range";
|
||||
import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
|
||||
import { MergedDateDisplay } from "@/components/dropdowns/merged-date";
|
||||
// hooks
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { useCycle } from "@/hooks/store/use-cycle";
|
||||
import { useMember } from "@/hooks/store/use-member";
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
@@ -109,25 +102,11 @@ export const CycleListItemAction = observer(function CycleListItemAction(props:
|
||||
e.preventDefault();
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
const addToFavoritePromise = addCycleToFavorites(workspaceSlug?.toString(), projectId.toString(), cycleId)
|
||||
.then(() => {
|
||||
const addToFavoritePromise = addCycleToFavorites(workspaceSlug?.toString(), projectId.toString(), cycleId).then(
|
||||
() => {
|
||||
if (!isFavoriteMenuOpen) toggleFavoriteMenu(true);
|
||||
captureSuccess({
|
||||
eventName: CYCLE_TRACKER_EVENTS.favorite,
|
||||
payload: {
|
||||
id: cycleId,
|
||||
},
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
captureError({
|
||||
eventName: CYCLE_TRACKER_EVENTS.favorite,
|
||||
payload: {
|
||||
id: cycleId,
|
||||
},
|
||||
error,
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
setPromiseToast(addToFavoritePromise, {
|
||||
loading: t("project_cycles.action.favorite.loading"),
|
||||
@@ -146,24 +125,11 @@ export const CycleListItemAction = observer(function CycleListItemAction(props:
|
||||
e.preventDefault();
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
const removeFromFavoritePromise = removeCycleFromFavorites(workspaceSlug?.toString(), projectId.toString(), cycleId)
|
||||
.then(() => {
|
||||
captureSuccess({
|
||||
eventName: CYCLE_TRACKER_EVENTS.unfavorite,
|
||||
payload: {
|
||||
id: cycleId,
|
||||
},
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
captureError({
|
||||
eventName: CYCLE_TRACKER_EVENTS.unfavorite,
|
||||
payload: {
|
||||
id: cycleId,
|
||||
},
|
||||
error,
|
||||
});
|
||||
});
|
||||
const removeFromFavoritePromise = removeCycleFromFavorites(
|
||||
workspaceSlug?.toString(),
|
||||
projectId.toString(),
|
||||
cycleId
|
||||
);
|
||||
|
||||
setPromiseToast(removeFromFavoritePromise, {
|
||||
loading: t("project_cycles.action.unfavorite.loading"),
|
||||
@@ -319,7 +285,6 @@ export const CycleListItemAction = observer(function CycleListItemAction(props:
|
||||
)}
|
||||
{isEditingAllowed && !cycleDetails.archived_at && (
|
||||
<FavoriteStar
|
||||
data-ph-element={CYCLE_TRACKER_ELEMENTS.LIST_ITEM}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { mutate } from "swr";
|
||||
// types
|
||||
import { CYCLE_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import type { CycleDateCheckData, ICycle, TCycleTabOptions } from "@plane/types";
|
||||
// ui
|
||||
import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
|
||||
// hooks
|
||||
import { renderFormattedPayloadDate } from "@plane/utils";
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { useCycle } from "@/hooks/store/use-cycle";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
import useKeypress from "@/hooks/use-keypress";
|
||||
@@ -62,12 +60,6 @@ export function CycleCreateUpdateModal(props: CycleModalProps) {
|
||||
title: "Success!",
|
||||
message: "Cycle created successfully.",
|
||||
});
|
||||
captureSuccess({
|
||||
eventName: CYCLE_TRACKER_EVENTS.create,
|
||||
payload: {
|
||||
id: res.id,
|
||||
},
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
setToast({
|
||||
@@ -75,10 +67,6 @@ export function CycleCreateUpdateModal(props: CycleModalProps) {
|
||||
title: "Error!",
|
||||
message: err?.detail ?? "Error in creating cycle. Please try again.",
|
||||
});
|
||||
captureError({
|
||||
eventName: CYCLE_TRACKER_EVENTS.create,
|
||||
error: err,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -88,12 +76,6 @@ export function CycleCreateUpdateModal(props: CycleModalProps) {
|
||||
const selectedProjectId = payload.project_id ?? projectId.toString();
|
||||
await updateCycleDetails(workspaceSlug, selectedProjectId, cycleId, payload)
|
||||
.then((res) => {
|
||||
captureSuccess({
|
||||
eventName: CYCLE_TRACKER_EVENTS.update,
|
||||
payload: {
|
||||
id: res.id,
|
||||
},
|
||||
});
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: "Success!",
|
||||
@@ -106,10 +88,6 @@ export function CycleCreateUpdateModal(props: CycleModalProps) {
|
||||
title: "Error!",
|
||||
message: err?.detail ?? "Error in updating cycle. Please try again.",
|
||||
});
|
||||
captureError({
|
||||
eventName: CYCLE_TRACKER_EVENTS.update,
|
||||
error: err,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,24 +1,16 @@
|
||||
import { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { MoreHorizontal } from "lucide-react";
|
||||
|
||||
// ui
|
||||
import {
|
||||
CYCLE_TRACKER_EVENTS,
|
||||
EUserPermissions,
|
||||
EUserPermissionsLevel,
|
||||
CYCLE_TRACKER_ELEMENTS,
|
||||
} from "@plane/constants";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { IconButton } from "@plane/propel/icon-button";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import type { TContextMenuItem } from "@plane/ui";
|
||||
import { ContextMenu, CustomMenu } from "@plane/ui";
|
||||
import { copyUrlToClipboard, cn } from "@plane/utils";
|
||||
// helpers
|
||||
// hooks
|
||||
import { useCycleMenuItems } from "@/components/common/quick-actions-helper";
|
||||
import { captureClick, captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { useCycle } from "@/hooks/store/use-cycle";
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
@@ -76,12 +68,6 @@ export const CycleQuickActions = observer(function CycleQuickActions(props: Prop
|
||||
title: t("project_cycles.action.restore.success.title"),
|
||||
message: t("project_cycles.action.restore.success.description"),
|
||||
});
|
||||
captureSuccess({
|
||||
eventName: CYCLE_TRACKER_EVENTS.restore,
|
||||
payload: {
|
||||
id: cycleId,
|
||||
},
|
||||
});
|
||||
router.push(`/${workspaceSlug}/projects/${projectId}/archives/cycles`);
|
||||
})
|
||||
.catch(() => {
|
||||
@@ -90,12 +76,6 @@ export const CycleQuickActions = observer(function CycleQuickActions(props: Prop
|
||||
title: t("project_cycles.action.restore.failed.title"),
|
||||
message: t("project_cycles.action.restore.failed.description"),
|
||||
});
|
||||
captureError({
|
||||
eventName: CYCLE_TRACKER_EVENTS.restore,
|
||||
payload: {
|
||||
id: cycleId,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const menuResult = useCycleMenuItems({
|
||||
@@ -118,11 +98,7 @@ export const CycleQuickActions = observer(function CycleQuickActions(props: Prop
|
||||
const CONTEXT_MENU_ITEMS = MENU_ITEMS.map(function CONTEXT_MENU_ITEMS(item) {
|
||||
return {
|
||||
...item,
|
||||
|
||||
action: () => {
|
||||
captureClick({
|
||||
elementName: CYCLE_TRACKER_ELEMENTS.CONTEXT_MENU,
|
||||
});
|
||||
item.action();
|
||||
},
|
||||
};
|
||||
@@ -170,9 +146,6 @@ export const CycleQuickActions = observer(function CycleQuickActions(props: Prop
|
||||
<CustomMenu.MenuItem
|
||||
key={item.key}
|
||||
onClick={() => {
|
||||
captureClick({
|
||||
elementName: CYCLE_TRACKER_ELEMENTS.QUICK_ACTIONS,
|
||||
});
|
||||
item.action();
|
||||
}}
|
||||
className={cn(
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import type { FC } from "react";
|
||||
import { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// ui
|
||||
import { PROJECT_SETTINGS_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { Button } from "@plane/propel/button";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
|
||||
// hooks
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { useProjectEstimates } from "@/hooks/store/estimates";
|
||||
import { useEstimate } from "@/hooks/store/estimates/use-estimate";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
@@ -34,18 +31,11 @@ export const DeleteEstimateModal = observer(function DeleteEstimateModal(props:
|
||||
try {
|
||||
if (!workspaceSlug || !projectId || !estimateId) return;
|
||||
setButtonLoader(true);
|
||||
|
||||
await deleteEstimate(workspaceSlug, projectId, estimateId);
|
||||
if (areEstimateEnabledByProjectId(projectId)) {
|
||||
await updateProject(workspaceSlug, projectId, { estimate: null });
|
||||
}
|
||||
setButtonLoader(false);
|
||||
captureSuccess({
|
||||
eventName: PROJECT_SETTINGS_TRACKER_EVENTS.estimate_deleted,
|
||||
payload: {
|
||||
id: estimateId,
|
||||
},
|
||||
});
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: "Estimate deleted",
|
||||
@@ -54,12 +44,6 @@ export const DeleteEstimateModal = observer(function DeleteEstimateModal(props:
|
||||
handleClose();
|
||||
} catch (error) {
|
||||
setButtonLoader(false);
|
||||
captureError({
|
||||
eventName: PROJECT_SETTINGS_TRACKER_EVENTS.estimate_deleted,
|
||||
payload: {
|
||||
id: estimateId,
|
||||
},
|
||||
});
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Estimate creation failed",
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import type { FC } from "react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { PROJECT_SETTINGS_TRACKER_ELEMENTS, PROJECT_SETTINGS_TRACKER_EVENTS } from "@plane/constants";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
// components
|
||||
import { DetailedEmptyState } from "@/components/empty-state/detailed-empty-state-root";
|
||||
// helpers
|
||||
import { captureElementAndEvent } from "@/helpers/event-tracker.helper";
|
||||
|
||||
type TEstimateEmptyScreen = {
|
||||
onButtonClick: () => void;
|
||||
@@ -30,15 +26,6 @@ export function EstimateEmptyScreen(props: TEstimateEmptyScreen) {
|
||||
text: t("project_settings.empty_state.estimates.primary_button"),
|
||||
onClick: () => {
|
||||
onButtonClick();
|
||||
captureElementAndEvent({
|
||||
element: {
|
||||
elementName: PROJECT_SETTINGS_TRACKER_ELEMENTS.ESTIMATES_EMPTY_STATE_CREATE_BUTTON,
|
||||
},
|
||||
event: {
|
||||
eventName: PROJECT_SETTINGS_TRACKER_EVENTS.estimate_created,
|
||||
state: "SUCCESS",
|
||||
},
|
||||
});
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import type { FC } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { PROJECT_SETTINGS_TRACKER_ELEMENTS, PROJECT_SETTINGS_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import { ToggleSwitch } from "@plane/ui";
|
||||
// hooks
|
||||
import { captureElementAndEvent } from "@/helpers/event-tracker.helper";
|
||||
import { useProjectEstimates } from "@/hooks/store/estimates";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
// i18n
|
||||
@@ -32,15 +29,6 @@ export const EstimateDisableSwitch = observer(function EstimateDisableSwitch(pro
|
||||
await updateProject(workspaceSlug, projectId, {
|
||||
estimate: currentProjectActiveEstimate ? null : currentActiveEstimateId,
|
||||
});
|
||||
captureElementAndEvent({
|
||||
element: {
|
||||
elementName: PROJECT_SETTINGS_TRACKER_ELEMENTS.ESTIMATES_TOGGLE_BUTTON,
|
||||
},
|
||||
event: {
|
||||
eventName: PROJECT_SETTINGS_TRACKER_EVENTS.estimates_toggle,
|
||||
state: "SUCCESS",
|
||||
},
|
||||
});
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: currentProjectActiveEstimate
|
||||
@@ -51,15 +39,6 @@ export const EstimateDisableSwitch = observer(function EstimateDisableSwitch(pro
|
||||
: t("project_settings.estimates.toasts.enabled.success.message"),
|
||||
});
|
||||
} catch (err) {
|
||||
captureElementAndEvent({
|
||||
element: {
|
||||
elementName: PROJECT_SETTINGS_TRACKER_ELEMENTS.ESTIMATES_TOGGLE_BUTTON,
|
||||
},
|
||||
event: {
|
||||
eventName: PROJECT_SETTINGS_TRACKER_EVENTS.estimates_toggle,
|
||||
state: "ERROR",
|
||||
},
|
||||
});
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: t("project_settings.estimates.toasts.disabled.error.title"),
|
||||
|
||||
@@ -8,8 +8,6 @@ import {
|
||||
EUserPermissionsLevel,
|
||||
EXPORTERS_LIST,
|
||||
// ISSUE_DISPLAY_FILTERS_BY_PAGE,
|
||||
WORKSPACE_SETTINGS_TRACKER_EVENTS,
|
||||
WORKSPACE_SETTINGS_TRACKER_ELEMENTS,
|
||||
} from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { Button } from "@plane/propel/button";
|
||||
@@ -20,7 +18,6 @@ import type { TWorkItemFilterExpression } from "@plane/types";
|
||||
import { CustomSearchSelect, CustomSelect } from "@plane/ui";
|
||||
// import { WorkspaceLevelWorkItemFiltersHOC } from "@/components/work-item-filters/filters-hoc/workspace-level";
|
||||
// import { WorkItemFiltersRow } from "@/components/work-item-filters/filters-row";
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
import { useUser, useUserPermissions } from "@/hooks/store/user";
|
||||
import { ProjectExportService } from "@/services/project/project-export.service";
|
||||
@@ -105,12 +102,6 @@ export const ExportForm = observer(function ExportForm(props: Props) {
|
||||
await projectExportService.csvExport(workspaceSlug, payload);
|
||||
mutateServices();
|
||||
setExportLoading(false);
|
||||
captureSuccess({
|
||||
eventName: WORKSPACE_SETTINGS_TRACKER_EVENTS.csv_exported,
|
||||
payload: {
|
||||
provider: formData.provider.provider,
|
||||
},
|
||||
});
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: t("workspace_settings.settings.exports.modal.toasts.success.title"),
|
||||
@@ -127,13 +118,6 @@ export const ExportForm = observer(function ExportForm(props: Props) {
|
||||
});
|
||||
} catch (error) {
|
||||
setExportLoading(false);
|
||||
captureError({
|
||||
eventName: WORKSPACE_SETTINGS_TRACKER_EVENTS.csv_exported,
|
||||
payload: {
|
||||
provider: formData.provider.provider,
|
||||
},
|
||||
error: error as Error,
|
||||
});
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: t("error"),
|
||||
@@ -258,12 +242,7 @@ export const ExportForm = observer(function ExportForm(props: Props) {
|
||||
/>
|
||||
</div> */}
|
||||
<div className="flex items-center justify-between">
|
||||
<Button
|
||||
variant="primary"
|
||||
type="submit"
|
||||
loading={exportLoading}
|
||||
data-ph-element={WORKSPACE_SETTINGS_TRACKER_ELEMENTS.EXPORT_BUTTON}
|
||||
>
|
||||
<Button variant="primary" type="submit" loading={exportLoading}>
|
||||
{exportLoading ? `${t("workspace_settings.settings.exports.exporting")}...` : t("export")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import { useEffect } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { USER_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
// ui
|
||||
import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
|
||||
// components
|
||||
import { ProductUpdatesFooter } from "@/components/global";
|
||||
// helpers
|
||||
import { captureView } from "@/helpers/event-tracker.helper";
|
||||
// plane web components
|
||||
import { ProductUpdatesChangelog } from "@/plane-web/components/global/product-updates/changelog";
|
||||
import { ProductUpdatesHeader } from "@/plane-web/components/global/product-updates/header";
|
||||
@@ -19,12 +15,6 @@ export type ProductUpdatesModalProps = {
|
||||
export const ProductUpdatesModal = observer(function ProductUpdatesModal(props: ProductUpdatesModalProps) {
|
||||
const { isOpen, handleClose } = props;
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
captureView({ elementName: USER_TRACKER_ELEMENTS.PRODUCT_CHANGELOG_MODAL });
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
return (
|
||||
<ModalCore isOpen={isOpen} handleClose={handleClose} position={EModalPosition.CENTER} width={EModalWidth.XXXXL}>
|
||||
<ProductUpdatesHeader />
|
||||
|
||||
@@ -2,10 +2,7 @@ import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import useSWR from "swr";
|
||||
// plane imports
|
||||
import { PRODUCT_TOUR_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { ContentWrapper } from "@plane/ui";
|
||||
// helpers
|
||||
import { captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
// hooks
|
||||
import { useHome } from "@/hooks/store/use-home";
|
||||
import { useUserProfile, useUser } from "@/hooks/store/user";
|
||||
@@ -33,19 +30,12 @@ export const WorkspaceHomeView = observer(function WorkspaceHomeView() {
|
||||
}
|
||||
);
|
||||
|
||||
const handleTourCompleted = () => {
|
||||
updateTourCompleted()
|
||||
.then(() => {
|
||||
captureSuccess({
|
||||
eventName: PRODUCT_TOUR_TRACKER_EVENTS.complete,
|
||||
payload: {
|
||||
user_id: currentUser?.id,
|
||||
},
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
const handleTourCompleted = async () => {
|
||||
try {
|
||||
await updateTourCompleted();
|
||||
} catch (error) {
|
||||
console.error("Error updating tour completed", error);
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: refactor loader implementation
|
||||
|
||||
@@ -5,14 +5,12 @@ import Link from "next/link";
|
||||
import { useParams } from "next/navigation";
|
||||
import { Hotel } from "lucide-react";
|
||||
// plane ui
|
||||
import { EUserPermissions, EUserPermissionsLevel, PROJECT_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useLocalStorage } from "@plane/hooks";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { MembersPropertyIcon, CheckIcon, ProjectIcon, CloseIcon } from "@plane/propel/icons";
|
||||
import { cn, getFileURL } from "@plane/utils";
|
||||
// helpers
|
||||
// hooks
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
import { useCommandPalette } from "@/hooks/store/use-command-palette";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
import { useWorkspace } from "@/hooks/store/use-workspace";
|
||||
@@ -57,7 +55,6 @@ export const NoProjectsEmptyState = observer(function NoProjectsEmptyState() {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
toggleCreateProjectModal(true);
|
||||
captureClick({ elementName: PROJECT_TRACKER_ELEMENTS.EMPTY_STATE_CREATE_PROJECT_BUTTON });
|
||||
},
|
||||
disabled: !canCreateProject,
|
||||
},
|
||||
|
||||
@@ -2,7 +2,6 @@ import type { Dispatch, SetStateAction } from "react";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// plane imports
|
||||
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||
import type { EditorRefApi } from "@plane/editor";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import type { TIssue, TNameDescriptionLoader } from "@plane/types";
|
||||
@@ -17,8 +16,6 @@ import type { TIssueOperations } from "@/components/issues/issue-detail";
|
||||
import { IssueActivity } from "@/components/issues/issue-detail/issue-activity";
|
||||
import { IssueReaction } from "@/components/issues/issue-detail/reactions";
|
||||
import { IssueTitleInput } from "@/components/issues/title-input";
|
||||
// helpers
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
// hooks
|
||||
import { useIssueDetail } from "@/hooks/store/use-issue-detail";
|
||||
import { useMember } from "@/hooks/store/use-member";
|
||||
@@ -102,10 +99,6 @@ export const InboxIssueMainContent = observer(function InboxIssueMainContent(pro
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
message: "Work item deleted successfully",
|
||||
});
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.delete,
|
||||
payload: { id: _issueId },
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Error in deleting work item:", error);
|
||||
setToast({
|
||||
@@ -113,47 +106,24 @@ export const InboxIssueMainContent = observer(function InboxIssueMainContent(pro
|
||||
type: TOAST_TYPE.ERROR,
|
||||
message: "Work item delete failed",
|
||||
});
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.delete,
|
||||
payload: { id: _issueId },
|
||||
error: error as Error,
|
||||
});
|
||||
}
|
||||
},
|
||||
update: async (_workspaceSlug: string, _projectId: string, _issueId: string, data: Partial<TIssue>) => {
|
||||
try {
|
||||
await inboxIssue.updateIssue(data);
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: _issueId },
|
||||
});
|
||||
} catch (error) {
|
||||
setToast({
|
||||
title: "Work item update failed",
|
||||
type: TOAST_TYPE.ERROR,
|
||||
message: "Work item update failed",
|
||||
});
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: _issueId },
|
||||
error: error as Error,
|
||||
});
|
||||
}
|
||||
},
|
||||
archive: async (workspaceSlug: string, projectId: string, issueId: string) => {
|
||||
try {
|
||||
await archiveIssue(workspaceSlug, projectId, issueId);
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.archive,
|
||||
payload: { id: issueId },
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error in archiving issue:", error);
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.archive,
|
||||
payload: { id: issueId },
|
||||
error: error as Error,
|
||||
});
|
||||
}
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { FC, FormEvent } from "react";
|
||||
import type { FormEvent } from "react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// plane imports
|
||||
import { ETabIndices, WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { ETabIndices } from "@plane/constants";
|
||||
import type { EditorRefApi } from "@plane/editor";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { Button } from "@plane/propel/button";
|
||||
@@ -10,8 +10,6 @@ import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import type { TIssue } from "@plane/types";
|
||||
import { ToggleSwitch } from "@plane/ui";
|
||||
import { renderFormattedPayloadDate, getTabIndex } from "@plane/utils";
|
||||
// helpers
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
// hooks
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
import { useProjectInbox } from "@/hooks/store/use-project-inbox";
|
||||
@@ -170,12 +168,6 @@ export const InboxIssueCreateRoot = observer(function InboxIssueCreateRoot(props
|
||||
descriptionEditorRef?.current?.clearEditor();
|
||||
setFormData(defaultIssueData);
|
||||
}
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.create,
|
||||
payload: {
|
||||
id: res?.issue?.id,
|
||||
},
|
||||
});
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: `Success!`,
|
||||
@@ -184,13 +176,6 @@ export const InboxIssueCreateRoot = observer(function InboxIssueCreateRoot(props
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.create,
|
||||
payload: {
|
||||
id: formData?.id,
|
||||
},
|
||||
error: error as Error,
|
||||
});
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: `Error!`,
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import Link from "next/link";
|
||||
import { useFormContext, Controller } from "react-hook-form";
|
||||
|
||||
import { PROJECT_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { PlusIcon } from "@plane/propel/icons";
|
||||
import type { IJiraImporterForm } from "@plane/types";
|
||||
// hooks
|
||||
@@ -11,7 +8,6 @@ import type { IJiraImporterForm } from "@plane/types";
|
||||
import { CustomSelect, Input } from "@plane/ui";
|
||||
// helpers
|
||||
import { checkEmailValidity } from "@plane/utils";
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
import { useCommandPalette } from "@/hooks/store/use-command-palette";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
// types
|
||||
@@ -199,9 +195,7 @@ export const JiraGetImportDetail = observer(function JiraGetImportDetail() {
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
data-ph-element={PROJECT_TRACKER_ELEMENTS.EMPTY_STATE_CREATE_PROJECT_BUTTON}
|
||||
onClick={() => {
|
||||
captureClick({ elementName: PROJECT_TRACKER_ELEMENTS.CREATE_PROJECT_JIRA_IMPORT_DETAIL_PAGE });
|
||||
toggleCreateProjectModal(true);
|
||||
}}
|
||||
className="flex cursor-pointer select-none items-center space-x-2 truncate rounded-sm px-1 py-1.5 text-secondary"
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { useMemo } from "react";
|
||||
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { setPromiseToast, TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import type { TIssueServiceType } from "@plane/types";
|
||||
import { EIssueServiceType } from "@plane/types";
|
||||
// hooks
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { useIssueDetail } from "@/hooks/store/use-issue-detail";
|
||||
// types
|
||||
import type { TAttachmentUploadStatus } from "@/store/issue/issue-details/attachment.store";
|
||||
@@ -36,34 +34,21 @@ export const useAttachmentOperations = (
|
||||
const attachmentOperations: TAttachmentOperations = useMemo(
|
||||
() => ({
|
||||
create: async (file) => {
|
||||
try {
|
||||
if (!workspaceSlug || !projectId || !issueId) throw new Error("Missing required fields");
|
||||
const attachmentUploadPromise = createAttachment(workspaceSlug, projectId, issueId, file);
|
||||
setPromiseToast(attachmentUploadPromise, {
|
||||
loading: "Uploading attachment...",
|
||||
success: {
|
||||
title: "Attachment uploaded",
|
||||
message: () => "The attachment has been successfully uploaded",
|
||||
},
|
||||
error: {
|
||||
title: "Attachment not uploaded",
|
||||
message: () => "The attachment could not be uploaded",
|
||||
},
|
||||
});
|
||||
if (!workspaceSlug || !projectId || !issueId) throw new Error("Missing required fields");
|
||||
const attachmentUploadPromise = createAttachment(workspaceSlug, projectId, issueId, file);
|
||||
setPromiseToast(attachmentUploadPromise, {
|
||||
loading: "Uploading attachment...",
|
||||
success: {
|
||||
title: "Attachment uploaded",
|
||||
message: () => "The attachment has been successfully uploaded",
|
||||
},
|
||||
error: {
|
||||
title: "Attachment not uploaded",
|
||||
message: () => "The attachment could not be uploaded",
|
||||
},
|
||||
});
|
||||
|
||||
await attachmentUploadPromise;
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.attachment.add,
|
||||
payload: { id: issueId },
|
||||
});
|
||||
} catch (error) {
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.attachment.add,
|
||||
payload: { id: issueId },
|
||||
error: error as Error,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
await attachmentUploadPromise;
|
||||
},
|
||||
remove: async (attachmentId) => {
|
||||
try {
|
||||
@@ -74,16 +59,7 @@ export const useAttachmentOperations = (
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: "Attachment removed",
|
||||
});
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.attachment.remove,
|
||||
payload: { id: issueId },
|
||||
});
|
||||
} catch (error) {
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.attachment.remove,
|
||||
payload: { id: issueId },
|
||||
error: error as Error,
|
||||
});
|
||||
} catch (_error) {
|
||||
setToast({
|
||||
message: "The Attachment could not be removed",
|
||||
type: TOAST_TYPE.ERROR,
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { useMemo } from "react";
|
||||
// plane imports
|
||||
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import type { TIssue, TIssueServiceType } from "@plane/types";
|
||||
import { EIssueServiceType } from "@plane/types";
|
||||
import { copyUrlToClipboard } from "@plane/utils";
|
||||
// hooks
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { useIssueDetail } from "@/hooks/store/use-issue-detail";
|
||||
|
||||
export type TRelationIssueOperations = {
|
||||
@@ -26,33 +24,23 @@ export const useRelationOperations = (
|
||||
|
||||
const issueOperations: TRelationIssueOperations = useMemo(
|
||||
() => ({
|
||||
copyLink: (path) => {
|
||||
copyUrlToClipboard(path).then(() => {
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: t("common.link_copied"),
|
||||
message: t("entity.link_copied_to_clipboard", { entity: entityName }),
|
||||
});
|
||||
copyLink: async (path) => {
|
||||
await copyUrlToClipboard(path);
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: t("common.link_copied"),
|
||||
message: t("entity.link_copied_to_clipboard", { entity: entityName }),
|
||||
});
|
||||
},
|
||||
update: async (workspaceSlug, projectId, issueId, data) => {
|
||||
try {
|
||||
await updateIssue(workspaceSlug, projectId, issueId, data);
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issueId },
|
||||
});
|
||||
setToast({
|
||||
title: t("toast.success"),
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
message: t("entity.update.success", { entity: entityName }),
|
||||
});
|
||||
} catch (error) {
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issueId },
|
||||
error: error as Error,
|
||||
});
|
||||
} catch (_error) {
|
||||
setToast({
|
||||
title: t("toast.error"),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
@@ -61,20 +49,7 @@ export const useRelationOperations = (
|
||||
}
|
||||
},
|
||||
remove: async (workspaceSlug, projectId, issueId) => {
|
||||
try {
|
||||
return removeIssue(workspaceSlug, projectId, issueId).then(() => {
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.delete,
|
||||
payload: { id: issueId },
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.delete,
|
||||
payload: { id: issueId },
|
||||
error: error as Error,
|
||||
});
|
||||
}
|
||||
return removeIssue(workspaceSlug, projectId, issueId);
|
||||
},
|
||||
}),
|
||||
[entityName, removeIssue, t, updateIssue]
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { useMemo } from "react";
|
||||
import { useParams } from "next/navigation";
|
||||
// plane imports
|
||||
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import type { TIssueServiceType, TSubIssueOperations } from "@plane/types";
|
||||
import { EIssueServiceType } from "@plane/types";
|
||||
import { copyUrlToClipboard } from "@plane/utils";
|
||||
// hooks
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { useIssueDetail } from "@/hooks/store/use-issue-detail";
|
||||
import { useProjectState } from "@/hooks/store/use-project-state";
|
||||
// plane web helpers
|
||||
@@ -39,18 +37,17 @@ export const useSubIssueOperations = (issueServiceType: TIssueServiceType): TSub
|
||||
|
||||
const subIssueOperations: TSubIssueOperations = useMemo(
|
||||
() => ({
|
||||
copyLink: (path) => {
|
||||
copyUrlToClipboard(path).then(() => {
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: t("common.link_copied"),
|
||||
message: t("entity.link_copied_to_clipboard", {
|
||||
entity:
|
||||
issueServiceType === EIssueServiceType.ISSUES
|
||||
? t("common.sub_work_items", { count: 1 })
|
||||
: t("issue.label", { count: 1 }),
|
||||
}),
|
||||
});
|
||||
copyLink: async (path) => {
|
||||
await copyUrlToClipboard(path);
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: t("common.link_copied"),
|
||||
message: t("entity.link_copied_to_clipboard", {
|
||||
entity:
|
||||
issueServiceType === EIssueServiceType.ISSUES
|
||||
? t("common.sub_work_items", { count: 1 })
|
||||
: t("issue.label", { count: 1 }),
|
||||
}),
|
||||
});
|
||||
},
|
||||
fetchSubIssues: async (workspaceSlug, projectId, parentIssueId) => {
|
||||
@@ -131,22 +128,13 @@ export const useSubIssueOperations = (issueServiceType: TIssueServiceType): TSub
|
||||
}
|
||||
}
|
||||
}
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.sub_issue.update,
|
||||
payload: { id: issueId, parent_id: parentIssueId },
|
||||
});
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: t("toast.success"),
|
||||
message: t("sub_work_item.update.success"),
|
||||
});
|
||||
setSubIssueHelpers(parentIssueId, "issue_loader", issueId);
|
||||
} catch (error) {
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.sub_issue.update,
|
||||
payload: { id: issueId, parent_id: parentIssueId },
|
||||
error: error as Error,
|
||||
});
|
||||
} catch (_error) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: t("toast.error"),
|
||||
@@ -178,17 +166,8 @@ export const useSubIssueOperations = (issueServiceType: TIssueServiceType): TSub
|
||||
: t("issue.label", { count: 1 }),
|
||||
}),
|
||||
});
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.sub_issue.remove,
|
||||
payload: { id: issueId, parent_id: parentIssueId },
|
||||
});
|
||||
setSubIssueHelpers(parentIssueId, "issue_loader", issueId);
|
||||
} catch (error) {
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.sub_issue.remove,
|
||||
payload: { id: issueId, parent_id: parentIssueId },
|
||||
error: error as Error,
|
||||
});
|
||||
} catch (_error) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: t("toast.error"),
|
||||
@@ -205,18 +184,9 @@ export const useSubIssueOperations = (issueServiceType: TIssueServiceType): TSub
|
||||
try {
|
||||
setSubIssueHelpers(parentIssueId, "issue_loader", issueId);
|
||||
return deleteSubIssue(workspaceSlug, projectId, parentIssueId, issueId).then(() => {
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.sub_issue.delete,
|
||||
payload: { id: issueId, parent_id: parentIssueId },
|
||||
});
|
||||
setSubIssueHelpers(parentIssueId, "issue_loader", issueId);
|
||||
});
|
||||
} catch (error) {
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.sub_issue.delete,
|
||||
payload: { id: issueId, parent_id: parentIssueId },
|
||||
error: error as Error,
|
||||
});
|
||||
} catch (_error) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: t("toast.error"),
|
||||
|
||||
-5
@@ -1,7 +1,5 @@
|
||||
import type { FC } from "react";
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
// plane imports
|
||||
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
@@ -9,7 +7,6 @@ import { PlusIcon, WorkItemsIcon } from "@plane/propel/icons";
|
||||
import type { TIssue, TIssueServiceType } from "@plane/types";
|
||||
import { CustomMenu } from "@plane/ui";
|
||||
// hooks
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
import { useIssueDetail } from "@/hooks/store/use-issue-detail";
|
||||
|
||||
type Props = {
|
||||
@@ -54,13 +51,11 @@ export const SubIssuesActionButton = observer(function SubIssuesActionButton(pro
|
||||
};
|
||||
|
||||
const handleCreateNew = () => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_EVENTS.sub_issue.create });
|
||||
handleIssueCrudState("create", issueId, null);
|
||||
toggleCreateIssueModal(true);
|
||||
};
|
||||
|
||||
const handleAddExisting = () => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_EVENTS.sub_issue.add_existing });
|
||||
handleIssueCrudState("existing", issueId, null);
|
||||
toggleSubIssuesModal(issue.id);
|
||||
};
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
import { useRef } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// plane imports
|
||||
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { CopyLinkIcon } from "@plane/propel/icons";
|
||||
import { IconButton } from "@plane/propel/icon-button";
|
||||
import { LinkIcon } from "@plane/propel/icons";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
import { EIssuesStoreType } from "@plane/types";
|
||||
import { generateWorkItemLink, copyTextToClipboard } from "@plane/utils";
|
||||
// helpers
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
// hooks
|
||||
import { useIssueDetail } from "@/hooks/store/use-issue-detail";
|
||||
import { useIssues } from "@/hooks/store/use-issues";
|
||||
@@ -70,15 +66,21 @@ export const IssueDetailQuickActions = observer(function IssueDetailQuickActions
|
||||
});
|
||||
|
||||
// handlers
|
||||
const handleCopyText = () => {
|
||||
const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
|
||||
copyTextToClipboard(`${originURL}${workItemLink}`).then(() => {
|
||||
const handleCopyText = async () => {
|
||||
try {
|
||||
const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
|
||||
await copyTextToClipboard(`${originURL}${workItemLink}`);
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: t("common.link_copied"),
|
||||
message: t("common.copied_to_clipboard"),
|
||||
});
|
||||
});
|
||||
} catch (_error) {
|
||||
setToast({
|
||||
title: t("toast.error"),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteIssue = async () => {
|
||||
@@ -88,24 +90,14 @@ export const IssueDetailQuickActions = observer(function IssueDetailQuickActions
|
||||
? `/${workspaceSlug}/projects/${projectId}/archives/issues`
|
||||
: `/${workspaceSlug}/projects/${projectId}/issues`;
|
||||
|
||||
return deleteIssue(workspaceSlug, projectId, issueId).then(() => {
|
||||
router.push(redirectionPath);
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.delete,
|
||||
payload: { id: issueId },
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
await deleteIssue(workspaceSlug, projectId, issueId);
|
||||
router.push(redirectionPath);
|
||||
} catch (_error) {
|
||||
setToast({
|
||||
title: t("toast.error "),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
message: t("entity.delete.failed", { entity: t("issue.label", { count: 1 }) }),
|
||||
});
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.delete,
|
||||
payload: { id: issueId },
|
||||
error: error as Error,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -113,38 +105,32 @@ export const IssueDetailQuickActions = observer(function IssueDetailQuickActions
|
||||
try {
|
||||
await archiveIssue(workspaceSlug, projectId, issueId);
|
||||
router.push(`/${workspaceSlug}/projects/${projectId}/issues`);
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.archive,
|
||||
payload: { id: issueId },
|
||||
});
|
||||
} catch (error) {
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.archive,
|
||||
payload: { id: issueId },
|
||||
error: error as Error,
|
||||
} catch (_error) {
|
||||
setToast({
|
||||
title: t("toast.error"),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
message: t("issue.archive.failed.message"),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleRestore = async () => {
|
||||
if (!workspaceSlug || !projectId || !issueId) return;
|
||||
|
||||
await restoreIssue(workspaceSlug.toString(), projectId.toString(), issueId.toString())
|
||||
.then(() => {
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: t("issue.restore.success.title"),
|
||||
message: t("issue.restore.success.message"),
|
||||
});
|
||||
router.push(workItemLink);
|
||||
})
|
||||
.catch(() => {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: t("toast.error"),
|
||||
message: t("issue.restore.failed.message"),
|
||||
});
|
||||
try {
|
||||
await restoreIssue(workspaceSlug.toString(), projectId.toString(), issueId.toString());
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: t("issue.restore.success.title"),
|
||||
message: t("issue.restore.success.message"),
|
||||
});
|
||||
router.push(workItemLink);
|
||||
} catch (_error) {
|
||||
setToast({
|
||||
title: t("toast.error"),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
message: t("issue.restore.failed.message"),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import type { FC } from "react";
|
||||
import { useMemo } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// plane imports
|
||||
import { EUserPermissions, EUserPermissionsLevel, WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { TOAST_TYPE, setPromiseToast, setToast } from "@plane/propel/toast";
|
||||
import type { TIssue } from "@plane/types";
|
||||
@@ -12,7 +11,6 @@ import emptyIssue from "@/app/assets/empty-state/issue.svg?url";
|
||||
// components
|
||||
import { EmptyState } from "@/components/common/empty-state";
|
||||
// hooks
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { useAppTheme } from "@/hooks/store/use-app-theme";
|
||||
import { useIssueDetail } from "@/hooks/store/use-issue-detail";
|
||||
import { useIssues } from "@/hooks/store/use-issues";
|
||||
@@ -90,17 +88,8 @@ export const IssueDetailRoot = observer(function IssueDetailRoot(props: TIssueDe
|
||||
update: async (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) => {
|
||||
try {
|
||||
await updateIssue(workspaceSlug, projectId, issueId, data);
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issueId },
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Error in updating issue:", error);
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issueId },
|
||||
error: error as Error,
|
||||
});
|
||||
setToast({
|
||||
title: t("common.error.label"),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
@@ -117,10 +106,6 @@ export const IssueDetailRoot = observer(function IssueDetailRoot(props: TIssueDe
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
message: t("entity.delete.success", { entity: t("issue.label") }),
|
||||
});
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.delete,
|
||||
payload: { id: issueId },
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Error in deleting issue:", error);
|
||||
setToast({
|
||||
@@ -128,67 +113,35 @@ export const IssueDetailRoot = observer(function IssueDetailRoot(props: TIssueDe
|
||||
type: TOAST_TYPE.ERROR,
|
||||
message: t("entity.delete.failed", { entity: t("issue.label") }),
|
||||
});
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.delete,
|
||||
payload: { id: issueId },
|
||||
error: error as Error,
|
||||
});
|
||||
}
|
||||
},
|
||||
archive: async (workspaceSlug: string, projectId: string, issueId: string) => {
|
||||
try {
|
||||
await archiveIssue(workspaceSlug, projectId, issueId);
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.archive,
|
||||
payload: { id: issueId },
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Error in archiving issue:", error);
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.archive,
|
||||
payload: { id: issueId },
|
||||
error: error as Error,
|
||||
});
|
||||
}
|
||||
},
|
||||
addCycleToIssue: async (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => {
|
||||
try {
|
||||
await addCycleToIssue(workspaceSlug, projectId, cycleId, issueId);
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issueId },
|
||||
});
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: t("common.error.label"),
|
||||
message: t("issue.add.cycle.failed"),
|
||||
});
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issueId },
|
||||
error: error as Error,
|
||||
});
|
||||
}
|
||||
},
|
||||
addIssueToCycle: async (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => {
|
||||
try {
|
||||
await addIssueToCycle(workspaceSlug, projectId, cycleId, issueIds);
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issueId },
|
||||
});
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: t("common.error.label"),
|
||||
message: t("issue.add.cycle.failed"),
|
||||
});
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issueId },
|
||||
error: error as Error,
|
||||
});
|
||||
}
|
||||
},
|
||||
removeIssueFromCycle: async (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => {
|
||||
@@ -206,16 +159,8 @@ export const IssueDetailRoot = observer(function IssueDetailRoot(props: TIssueDe
|
||||
},
|
||||
});
|
||||
await removeFromCyclePromise;
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issueId },
|
||||
});
|
||||
} catch (error) {
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issueId },
|
||||
error: error as Error,
|
||||
});
|
||||
console.log("Error in removing issue from cycle:", error);
|
||||
}
|
||||
},
|
||||
removeIssueFromModule: async (workspaceSlug: string, projectId: string, moduleId: string, issueId: string) => {
|
||||
@@ -233,16 +178,8 @@ export const IssueDetailRoot = observer(function IssueDetailRoot(props: TIssueDe
|
||||
},
|
||||
});
|
||||
await removeFromModulePromise;
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issueId },
|
||||
});
|
||||
} catch (error) {
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issueId },
|
||||
error: error as Error,
|
||||
});
|
||||
console.log("Error in removing issue from module:", error);
|
||||
}
|
||||
},
|
||||
changeModulesInIssue: async (
|
||||
@@ -253,10 +190,6 @@ export const IssueDetailRoot = observer(function IssueDetailRoot(props: TIssueDe
|
||||
removeModuleIds: string[]
|
||||
) => {
|
||||
const promise = await changeModulesInIssue(workspaceSlug, projectId, issueId, addModuleIds, removeModuleIds);
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issueId },
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
}),
|
||||
@@ -273,7 +206,6 @@ export const IssueDetailRoot = observer(function IssueDetailRoot(props: TIssueDe
|
||||
changeModulesInIssue,
|
||||
removeIssueFromModule,
|
||||
t,
|
||||
issueId,
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import type { ISearchIssueResponse } from "@plane/types";
|
||||
import { EIssuesStoreType, EUserProjectRoles } from "@plane/types";
|
||||
// components
|
||||
import { ExistingIssuesListModal } from "@/components/core/modals/existing-issues-list-modal";
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
import { useCommandPalette } from "@/hooks/store/use-command-palette";
|
||||
import { useCycle } from "@/hooks/store/use-cycle";
|
||||
import { useIssues } from "@/hooks/store/use-issues";
|
||||
@@ -107,7 +106,6 @@ export const CycleEmptyState = observer(function CycleEmptyState() {
|
||||
{
|
||||
label: t("project_empty_state.cycle_work_items.cta_primary"),
|
||||
onClick: () => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.EMPTY_STATE_ADD_BUTTON.CYCLE });
|
||||
toggleCreateIssueModal(true, EIssuesStoreType.CYCLE);
|
||||
},
|
||||
disabled: !canPerformEmptyStateActions,
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { observer } from "mobx-react";
|
||||
// plane imports
|
||||
import { EUserPermissionsLevel, WORK_ITEM_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { EmptyStateDetailed } from "@plane/propel/empty-state";
|
||||
import { EIssuesStoreType, EUserWorkspaceRoles } from "@plane/types";
|
||||
// hooks
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
import { useCommandPalette } from "@/hooks/store/use-command-palette";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
@@ -35,7 +34,6 @@ export const GlobalViewEmptyState = observer(function GlobalViewEmptyState() {
|
||||
label: t("workspace_projects.empty_state.no_projects.primary_button.text"),
|
||||
onClick: () => {
|
||||
toggleCreateProjectModal(true);
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.EMPTY_STATE_ADD_BUTTON.GLOBAL_VIEW });
|
||||
},
|
||||
disabled: !hasMemberLevelPermission,
|
||||
variant: "primary",
|
||||
@@ -55,7 +53,6 @@ export const GlobalViewEmptyState = observer(function GlobalViewEmptyState() {
|
||||
{
|
||||
label: t(`workspace_empty_state.views.cta_primary`),
|
||||
onClick: () => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.EMPTY_STATE_ADD_BUTTON.GLOBAL_VIEW });
|
||||
toggleCreateIssueModal(true, EIssuesStoreType.PROJECT);
|
||||
},
|
||||
disabled: !hasMemberLevelPermission,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// plane imports
|
||||
import { EUserPermissionsLevel, WORK_ITEM_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { EmptyStateDetailed } from "@plane/propel/empty-state";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
@@ -10,7 +10,6 @@ import type { ISearchIssueResponse } from "@plane/types";
|
||||
import { EIssuesStoreType, EUserProjectRoles } from "@plane/types";
|
||||
// components
|
||||
import { ExistingIssuesListModal } from "@/components/core/modals/existing-issues-list-modal";
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
// hooks
|
||||
import { useCommandPalette } from "@/hooks/store/use-command-palette";
|
||||
import { useIssues } from "@/hooks/store/use-issues";
|
||||
@@ -94,7 +93,6 @@ export const ModuleEmptyState = observer(function ModuleEmptyState() {
|
||||
{
|
||||
label: t("project_empty_state.module_work_items.cta_primary"),
|
||||
onClick: () => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.EMPTY_STATE_ADD_BUTTON.MODULE });
|
||||
toggleCreateIssueModal(true, EIssuesStoreType.MODULE);
|
||||
},
|
||||
disabled: !canPerformEmptyStateActions,
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// plane imports
|
||||
import { EUserPermissionsLevel, WORK_ITEM_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { EmptyStateDetailed } from "@plane/propel/empty-state";
|
||||
import { EIssuesStoreType, EUserProjectRoles } from "@plane/types";
|
||||
// components
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
// hooks
|
||||
import { useCommandPalette } from "@/hooks/store/use-command-palette";
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
@@ -54,7 +52,6 @@ export const ProjectEmptyState = observer(function ProjectEmptyState() {
|
||||
{
|
||||
label: t("project_empty_state.work_items.cta_primary"),
|
||||
onClick: () => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.EMPTY_STATE_ADD_BUTTON.WORK_ITEMS });
|
||||
toggleCreateIssueModal(true, EIssuesStoreType.PROJECT);
|
||||
},
|
||||
disabled: !canPerformEmptyStateActions,
|
||||
|
||||
@@ -3,8 +3,6 @@ import { observer } from "mobx-react";
|
||||
import { EUserPermissions, EUserPermissionsLevel, WORK_ITEM_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { EmptyStateDetailed } from "@plane/propel/empty-state";
|
||||
import { EIssuesStoreType } from "@plane/types";
|
||||
// components
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
// hooks
|
||||
import { useCommandPalette } from "@/hooks/store/use-command-palette";
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
@@ -30,7 +28,6 @@ export const ProjectViewEmptyState = observer(function ProjectViewEmptyState() {
|
||||
{
|
||||
label: "New work item",
|
||||
onClick: () => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.EMPTY_STATE_ADD_BUTTON.PROJECT_VIEW });
|
||||
toggleCreateIssueModal(true, EIssuesStoreType.PROJECT_VIEW);
|
||||
},
|
||||
disabled: !isCreatingIssueAllowed,
|
||||
|
||||
@@ -5,12 +5,10 @@ import { dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element
|
||||
import { autoScrollForElements } from "@atlaskit/pragmatic-drag-and-drop-auto-scroll/element";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { EIssueFilterType, EUserPermissions, EUserPermissionsLevel, WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { EIssueFilterType, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import type { EIssuesStoreType } from "@plane/types";
|
||||
import { EIssueServiceType, EIssueLayoutTypes } from "@plane/types";
|
||||
//constants
|
||||
//hooks
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { useIssueDetail } from "@/hooks/store/use-issue-detail";
|
||||
import { useIssues } from "@/hooks/store/use-issues";
|
||||
import { useKanbanView } from "@/hooks/store/use-kanban-view";
|
||||
@@ -200,23 +198,14 @@ export const BaseKanBanRoot = observer(function BaseKanBanRoot(props: IBaseKanBa
|
||||
|
||||
if (!draggedIssueId || !draggedIssue) return;
|
||||
|
||||
await removeIssue(draggedIssue.project_id, draggedIssueId)
|
||||
.then(() => {
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.delete,
|
||||
payload: { id: draggedIssueId },
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.delete,
|
||||
payload: { id: draggedIssueId },
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
setDeleteIssueModal(false);
|
||||
setDraggedIssueId(undefined);
|
||||
});
|
||||
try {
|
||||
await removeIssue(draggedIssue.project_id, draggedIssueId);
|
||||
setDeleteIssueModal(false);
|
||||
setDraggedIssueId(undefined);
|
||||
} catch (_error) {
|
||||
setDeleteIssueModal(false);
|
||||
setDraggedIssueId(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCollapsedGroups = useCallback(
|
||||
|
||||
@@ -3,7 +3,6 @@ import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// lucide icons
|
||||
import { Minimize2, Maximize2, Circle } from "lucide-react";
|
||||
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { PlusIcon } from "@plane/propel/icons";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import type { TIssue, ISearchIssueResponse, TIssueKanbanFilters, TIssueGroupByOptions } from "@plane/types";
|
||||
@@ -13,7 +12,6 @@ import { CustomMenu } from "@plane/ui";
|
||||
import { ExistingIssuesListModal } from "@/components/core/modals/existing-issues-list-modal";
|
||||
import { CreateUpdateIssueModal } from "@/components/issues/issue-modal/modal";
|
||||
// constants
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
|
||||
import { CreateUpdateEpicModal } from "@/plane-web/components/epics/epic-modal";
|
||||
// types
|
||||
@@ -75,7 +73,7 @@ export const HeaderGroupByCard = observer(function HeaderGroupByCard(props: IHea
|
||||
title: "Success!",
|
||||
message: "Work items added to the cycle successfully.",
|
||||
});
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
@@ -162,7 +160,6 @@ export const HeaderGroupByCard = observer(function HeaderGroupByCard(props: IHea
|
||||
>
|
||||
<CustomMenu.MenuItem
|
||||
onClick={() => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_EVENTS.create });
|
||||
setIsOpen(true);
|
||||
}}
|
||||
>
|
||||
@@ -170,7 +167,6 @@ export const HeaderGroupByCard = observer(function HeaderGroupByCard(props: IHea
|
||||
</CustomMenu.MenuItem>
|
||||
<CustomMenu.MenuItem
|
||||
onClick={() => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_EVENTS.add_existing });
|
||||
setOpenExistingIssueListModal(true);
|
||||
}}
|
||||
>
|
||||
@@ -181,7 +177,6 @@ export const HeaderGroupByCard = observer(function HeaderGroupByCard(props: IHea
|
||||
<button
|
||||
className="flex h-[20px] w-[20px] flex-shrink-0 cursor-pointer overflow-hidden transition-all hover:bg-layer-transparent-hover bg-layer-transparent rounded-sm items-center justify-center"
|
||||
onClick={() => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_EVENTS.create });
|
||||
setIsOpen(true);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -2,7 +2,6 @@ import { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { CircleDashed } from "lucide-react";
|
||||
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { PlusIcon } from "@plane/propel/icons";
|
||||
// types
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
@@ -15,7 +14,6 @@ import { ExistingIssuesListModal } from "@/components/core/modals/existing-issue
|
||||
import { MultipleSelectGroupAction } from "@/components/core/multiple-select";
|
||||
import { CreateUpdateIssueModal } from "@/components/issues/issue-modal/modal";
|
||||
// constants
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
|
||||
import type { TSelectionHelper } from "@/hooks/use-multiple-select";
|
||||
// plane-web
|
||||
@@ -132,7 +130,6 @@ export const HeaderGroupByCard = observer(function HeaderGroupByCard(props: IHea
|
||||
>
|
||||
<CustomMenu.MenuItem
|
||||
onClick={() => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_EVENTS.create });
|
||||
setIsOpen(true);
|
||||
}}
|
||||
>
|
||||
@@ -140,7 +137,6 @@ export const HeaderGroupByCard = observer(function HeaderGroupByCard(props: IHea
|
||||
</CustomMenu.MenuItem>
|
||||
<CustomMenu.MenuItem
|
||||
onClick={() => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_EVENTS.add_existing });
|
||||
setOpenExistingIssueListModal(true);
|
||||
}}
|
||||
>
|
||||
@@ -151,7 +147,6 @@ export const HeaderGroupByCard = observer(function HeaderGroupByCard(props: IHea
|
||||
<div
|
||||
className="flex h-5 w-5 flex-shrink-0 cursor-pointer items-center justify-center overflow-hidden rounded-xs transition-all hover:bg-layer-1"
|
||||
onClick={() => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_EVENTS.create });
|
||||
setIsOpen(true);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -5,8 +5,6 @@ import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// icons
|
||||
import { Paperclip } from "lucide-react";
|
||||
// types
|
||||
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||
// i18n
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { LinkIcon, StartDatePropertyIcon, ViewsIcon, DueDatePropertyIcon } from "@plane/propel/icons";
|
||||
@@ -29,8 +27,6 @@ import { MemberDropdown } from "@/components/dropdowns/member/dropdown";
|
||||
import { ModuleDropdown } from "@/components/dropdowns/module/dropdown";
|
||||
import { PriorityDropdown } from "@/components/dropdowns/priority";
|
||||
import { StateDropdown } from "@/components/dropdowns/state/dropdown";
|
||||
// helpers
|
||||
import { captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
// hooks
|
||||
import { useProjectEstimates } from "@/hooks/store/estimates";
|
||||
import { useIssues } from "@/hooks/store/use-issues";
|
||||
@@ -105,44 +101,20 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||
[workspaceSlug, issue, changeModulesInIssue, addCycleToIssue, removeCycleFromIssue]
|
||||
);
|
||||
|
||||
const handleState = (stateId: string) => {
|
||||
if (updateIssue)
|
||||
updateIssue(issue.project_id, issue.id, { state_id: stateId }).then(() => {
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issue.id },
|
||||
});
|
||||
});
|
||||
const handleState = async (stateId: string) => {
|
||||
if (updateIssue) await updateIssue(issue.project_id, issue.id, { state_id: stateId });
|
||||
};
|
||||
|
||||
const handlePriority = (value: TIssuePriorities) => {
|
||||
if (updateIssue)
|
||||
updateIssue(issue.project_id, issue.id, { priority: value }).then(() => {
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issue.id },
|
||||
});
|
||||
});
|
||||
const handlePriority = async (value: TIssuePriorities) => {
|
||||
if (updateIssue) await updateIssue(issue.project_id, issue.id, { priority: value });
|
||||
};
|
||||
|
||||
const handleLabel = (ids: string[]) => {
|
||||
if (updateIssue)
|
||||
updateIssue(issue.project_id, issue.id, { label_ids: ids }).then(() => {
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issue.id },
|
||||
});
|
||||
});
|
||||
const handleLabel = async (ids: string[]) => {
|
||||
if (updateIssue) await updateIssue(issue.project_id, issue.id, { label_ids: ids });
|
||||
};
|
||||
|
||||
const handleAssignee = (ids: string[]) => {
|
||||
if (updateIssue)
|
||||
updateIssue(issue.project_id, issue.id, { assignee_ids: ids }).then(() => {
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issue.id },
|
||||
});
|
||||
});
|
||||
const handleAssignee = async (ids: string[]) => {
|
||||
if (updateIssue) await updateIssue(issue.project_id, issue.id, { assignee_ids: ids });
|
||||
};
|
||||
|
||||
const handleModule = useCallback(
|
||||
@@ -157,11 +129,6 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||
else modulesToAdd.push(moduleId);
|
||||
if (modulesToAdd.length > 0) issueOperations.addModulesToIssue(modulesToAdd);
|
||||
if (modulesToRemove.length > 0) issueOperations.removeModulesFromIssue(modulesToRemove);
|
||||
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issue.id },
|
||||
});
|
||||
},
|
||||
[issueOperations, issue]
|
||||
);
|
||||
@@ -171,47 +138,22 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
|
||||
if (!issue || issue.cycle_id === cycleId) return;
|
||||
if (cycleId) issueOperations.addIssueToCycle?.(cycleId);
|
||||
else issueOperations.removeIssueFromCycle?.();
|
||||
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issue.id },
|
||||
});
|
||||
},
|
||||
[issue, issueOperations]
|
||||
);
|
||||
|
||||
const handleStartDate = (date: Date | null) => {
|
||||
const handleStartDate = async (date: Date | null) => {
|
||||
if (updateIssue)
|
||||
updateIssue(issue.project_id, issue.id, { start_date: date ? renderFormattedPayloadDate(date) : null }).then(
|
||||
() => {
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issue.id },
|
||||
});
|
||||
}
|
||||
);
|
||||
await updateIssue(issue.project_id, issue.id, { start_date: date ? renderFormattedPayloadDate(date) : null });
|
||||
};
|
||||
|
||||
const handleTargetDate = (date: Date | null) => {
|
||||
const handleTargetDate = async (date: Date | null) => {
|
||||
if (updateIssue)
|
||||
updateIssue(issue.project_id, issue.id, { target_date: date ? renderFormattedPayloadDate(date) : null }).then(
|
||||
() => {
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issue.id },
|
||||
});
|
||||
}
|
||||
);
|
||||
await updateIssue(issue.project_id, issue.id, { target_date: date ? renderFormattedPayloadDate(date) : null });
|
||||
};
|
||||
|
||||
const handleEstimate = (value: string | undefined) => {
|
||||
if (updateIssue)
|
||||
updateIssue(issue.project_id, issue.id, { estimate_point: value }).then(() => {
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: issue.id },
|
||||
});
|
||||
});
|
||||
const handleEstimate = async (value: string | undefined) => {
|
||||
if (updateIssue) await updateIssue(issue.project_id, issue.id, { estimate_point: value });
|
||||
};
|
||||
|
||||
const workItemLink = generateWorkItemLink({
|
||||
|
||||
@@ -3,14 +3,12 @@ import { omit } from "lodash-es";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// plane imports
|
||||
import { ARCHIVABLE_STATE_GROUPS, WORK_ITEM_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { ARCHIVABLE_STATE_GROUPS } from "@plane/constants";
|
||||
import type { TIssue } from "@plane/types";
|
||||
import { EIssuesStoreType } from "@plane/types";
|
||||
import type { TContextMenuItem } from "@plane/ui";
|
||||
import { ContextMenu, CustomMenu } from "@plane/ui";
|
||||
import { cn } from "@plane/utils";
|
||||
// hooks
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
import { useProjectState } from "@/hooks/store/use-project-state";
|
||||
// plane-web components
|
||||
@@ -88,9 +86,7 @@ export const AllIssueQuickActions = observer(function AllIssueQuickActions(props
|
||||
const CONTEXT_MENU_ITEMS = MENU_ITEMS.map(function CONTEXT_MENU_ITEMS(item) {
|
||||
return {
|
||||
...item,
|
||||
|
||||
onClick: () => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.QUICK_ACTIONS.GLOBAL_VIEW });
|
||||
item.action();
|
||||
},
|
||||
};
|
||||
@@ -180,7 +176,6 @@ export const AllIssueQuickActions = observer(function AllIssueQuickActions(props
|
||||
<CustomMenu.MenuItem
|
||||
key={nestedItem.key}
|
||||
onClick={() => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.QUICK_ACTIONS.GLOBAL_VIEW });
|
||||
nestedItem.action();
|
||||
}}
|
||||
className={cn(
|
||||
@@ -216,7 +211,6 @@ export const AllIssueQuickActions = observer(function AllIssueQuickActions(props
|
||||
<CustomMenu.MenuItem
|
||||
key={item.key}
|
||||
onClick={() => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.QUICK_ACTIONS.GLOBAL_VIEW });
|
||||
item.action();
|
||||
}}
|
||||
className={cn(
|
||||
|
||||
+1
-5
@@ -2,13 +2,11 @@ import { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// ui
|
||||
import { EUserPermissions, EUserPermissionsLevel, WORK_ITEM_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { EIssuesStoreType } from "@plane/types";
|
||||
import type { TContextMenuItem } from "@plane/ui";
|
||||
import { ContextMenu, CustomMenu } from "@plane/ui";
|
||||
import { cn } from "@plane/utils";
|
||||
// hooks
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
import { useIssues } from "@/hooks/store/use-issues";
|
||||
import { useUserPermissions } from "@/hooks/store/user";
|
||||
// local imports
|
||||
@@ -66,7 +64,6 @@ export const ArchivedIssueQuickActions = observer(function ArchivedIssueQuickAct
|
||||
...item,
|
||||
|
||||
onClick: () => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.QUICK_ACTIONS.ARCHIVED });
|
||||
item.action();
|
||||
},
|
||||
};
|
||||
@@ -99,7 +96,6 @@ export const ArchivedIssueQuickActions = observer(function ArchivedIssueQuickAct
|
||||
key={item.key}
|
||||
onClick={() => {
|
||||
item.action();
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.QUICK_ACTIONS.ARCHIVED });
|
||||
}}
|
||||
className={cn(
|
||||
"flex items-center gap-2",
|
||||
|
||||
+1
-11
@@ -3,19 +3,12 @@ import { omit } from "lodash-es";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// plane imports
|
||||
import {
|
||||
ARCHIVABLE_STATE_GROUPS,
|
||||
EUserPermissions,
|
||||
EUserPermissionsLevel,
|
||||
WORK_ITEM_TRACKER_ELEMENTS,
|
||||
} from "@plane/constants";
|
||||
import { ARCHIVABLE_STATE_GROUPS, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import type { TIssue } from "@plane/types";
|
||||
import { EIssuesStoreType } from "@plane/types";
|
||||
import type { TContextMenuItem } from "@plane/ui";
|
||||
import { ContextMenu, CustomMenu } from "@plane/ui";
|
||||
import { cn } from "@plane/utils";
|
||||
// hooks
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
import { useIssues } from "@/hooks/store/use-issues";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
import { useProjectState } from "@/hooks/store/use-project-state";
|
||||
@@ -107,7 +100,6 @@ export const CycleIssueQuickActions = observer(function CycleIssueQuickActions(p
|
||||
...item,
|
||||
|
||||
onClick: () => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.QUICK_ACTIONS.CYCLE });
|
||||
item.action();
|
||||
},
|
||||
};
|
||||
@@ -197,7 +189,6 @@ export const CycleIssueQuickActions = observer(function CycleIssueQuickActions(p
|
||||
<CustomMenu.MenuItem
|
||||
key={nestedItem.key}
|
||||
onClick={() => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.QUICK_ACTIONS.CYCLE });
|
||||
nestedItem.action();
|
||||
}}
|
||||
className={cn(
|
||||
@@ -233,7 +224,6 @@ export const CycleIssueQuickActions = observer(function CycleIssueQuickActions(p
|
||||
<CustomMenu.MenuItem
|
||||
key={item.key}
|
||||
onClick={() => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.QUICK_ACTIONS.CYCLE });
|
||||
item.action();
|
||||
}}
|
||||
className={cn(
|
||||
|
||||
+1
-11
@@ -4,19 +4,12 @@ import { observer } from "mobx-react";
|
||||
import { useParams, usePathname } from "next/navigation";
|
||||
import { Ellipsis } from "lucide-react";
|
||||
// plane imports
|
||||
import {
|
||||
ARCHIVABLE_STATE_GROUPS,
|
||||
EUserPermissions,
|
||||
EUserPermissionsLevel,
|
||||
WORK_ITEM_TRACKER_ELEMENTS,
|
||||
} from "@plane/constants";
|
||||
import { ARCHIVABLE_STATE_GROUPS, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import type { TIssue } from "@plane/types";
|
||||
import { EIssuesStoreType } from "@plane/types";
|
||||
import type { TContextMenuItem } from "@plane/ui";
|
||||
import { ContextMenu, CustomMenu } from "@plane/ui";
|
||||
import { cn } from "@plane/utils";
|
||||
// hooks
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
import { useIssues } from "@/hooks/store/use-issues";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
import { useProjectState } from "@/hooks/store/use-project-state";
|
||||
@@ -186,7 +179,6 @@ export const WorkItemDetailQuickActions = observer(function WorkItemDetailQuickA
|
||||
...item,
|
||||
|
||||
onClick: () => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.QUICK_ACTIONS.PROJECT_VIEW });
|
||||
item.action();
|
||||
},
|
||||
};
|
||||
@@ -286,7 +278,6 @@ export const WorkItemDetailQuickActions = observer(function WorkItemDetailQuickA
|
||||
<CustomMenu.MenuItem
|
||||
key={nestedItem.key}
|
||||
onClick={() => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.QUICK_ACTIONS.PROJECT_VIEW });
|
||||
nestedItem.action();
|
||||
}}
|
||||
className={cn(
|
||||
@@ -322,7 +313,6 @@ export const WorkItemDetailQuickActions = observer(function WorkItemDetailQuickA
|
||||
<CustomMenu.MenuItem
|
||||
key={item.key}
|
||||
onClick={() => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.QUICK_ACTIONS.PROJECT_VIEW });
|
||||
item.action();
|
||||
}}
|
||||
className={cn(
|
||||
|
||||
+1
-11
@@ -3,19 +3,12 @@ import { omit } from "lodash-es";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// plane imports
|
||||
import {
|
||||
ARCHIVABLE_STATE_GROUPS,
|
||||
EUserPermissions,
|
||||
EUserPermissionsLevel,
|
||||
WORK_ITEM_TRACKER_ELEMENTS,
|
||||
} from "@plane/constants";
|
||||
import { ARCHIVABLE_STATE_GROUPS, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import type { TIssue } from "@plane/types";
|
||||
import { EIssuesStoreType } from "@plane/types";
|
||||
import type { TContextMenuItem } from "@plane/ui";
|
||||
import { ContextMenu, CustomMenu } from "@plane/ui";
|
||||
import { cn } from "@plane/utils";
|
||||
// hooks
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
import { useIssues } from "@/hooks/store/use-issues";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
import { useProjectState } from "@/hooks/store/use-project-state";
|
||||
@@ -107,7 +100,6 @@ export const ModuleIssueQuickActions = observer(function ModuleIssueQuickActions
|
||||
...item,
|
||||
|
||||
onClick: () => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.QUICK_ACTIONS.MODULE });
|
||||
item.action();
|
||||
},
|
||||
};
|
||||
@@ -196,7 +188,6 @@ export const ModuleIssueQuickActions = observer(function ModuleIssueQuickActions
|
||||
<CustomMenu.MenuItem
|
||||
key={nestedItem.key}
|
||||
onClick={() => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.QUICK_ACTIONS.MODULE });
|
||||
nestedItem.action();
|
||||
}}
|
||||
className={cn(
|
||||
@@ -232,7 +223,6 @@ export const ModuleIssueQuickActions = observer(function ModuleIssueQuickActions
|
||||
<CustomMenu.MenuItem
|
||||
key={item.key}
|
||||
onClick={() => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.QUICK_ACTIONS.MODULE });
|
||||
item.action();
|
||||
}}
|
||||
className={cn(
|
||||
|
||||
+1
-11
@@ -3,19 +3,12 @@ import { omit } from "lodash-es";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// plane imports
|
||||
import {
|
||||
ARCHIVABLE_STATE_GROUPS,
|
||||
EUserPermissions,
|
||||
EUserPermissionsLevel,
|
||||
WORK_ITEM_TRACKER_ELEMENTS,
|
||||
} from "@plane/constants";
|
||||
import { ARCHIVABLE_STATE_GROUPS, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import type { TIssue } from "@plane/types";
|
||||
import { EIssuesStoreType } from "@plane/types";
|
||||
import type { TContextMenuItem } from "@plane/ui";
|
||||
import { ContextMenu, CustomMenu } from "@plane/ui";
|
||||
import { cn } from "@plane/utils";
|
||||
// hooks
|
||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||
import { useIssues } from "@/hooks/store/use-issues";
|
||||
import { useProject } from "@/hooks/store/use-project";
|
||||
import { useProjectState } from "@/hooks/store/use-project-state";
|
||||
@@ -108,7 +101,6 @@ export const ProjectIssueQuickActions = observer(function ProjectIssueQuickActio
|
||||
...item,
|
||||
|
||||
onClick: () => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.QUICK_ACTIONS.PROJECT_VIEW });
|
||||
item.action();
|
||||
},
|
||||
};
|
||||
@@ -197,7 +189,6 @@ export const ProjectIssueQuickActions = observer(function ProjectIssueQuickActio
|
||||
<CustomMenu.MenuItem
|
||||
key={nestedItem.key}
|
||||
onClick={() => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.QUICK_ACTIONS.PROJECT_VIEW });
|
||||
nestedItem.action();
|
||||
}}
|
||||
className={cn(
|
||||
@@ -233,7 +224,6 @@ export const ProjectIssueQuickActions = observer(function ProjectIssueQuickActio
|
||||
<CustomMenu.MenuItem
|
||||
key={item.key}
|
||||
onClick={() => {
|
||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.QUICK_ACTIONS.PROJECT_VIEW });
|
||||
item.action();
|
||||
}}
|
||||
className={cn(
|
||||
|
||||
@@ -4,16 +4,12 @@ import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import type { UseFormRegister } from "react-hook-form";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
// plane imports
|
||||
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { PlusIcon } from "@plane/propel/icons";
|
||||
import { setPromiseToast } from "@plane/propel/toast";
|
||||
import type { IProject, TIssue, EIssueLayoutTypes } from "@plane/types";
|
||||
import { cn, createIssuePayload } from "@plane/utils";
|
||||
// helpers
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
// plane web imports
|
||||
import { QuickAddIssueFormRoot } from "@/plane-web/components/issues/quick-add";
|
||||
// local imports
|
||||
@@ -128,20 +124,7 @@ export const QuickAddIssueRoot = observer(function QuickAddIssueRoot(props: TQui
|
||||
},
|
||||
});
|
||||
|
||||
await quickAddPromise
|
||||
.then((res) => {
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.create,
|
||||
payload: { id: res?.id },
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.create,
|
||||
payload: { id: payload.id },
|
||||
error: error as Error,
|
||||
});
|
||||
});
|
||||
await quickAddPromise;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import React, { useCallback } from "react";
|
||||
import { useCallback } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// types
|
||||
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||
import type { TIssue } from "@plane/types";
|
||||
// components
|
||||
import { CycleDropdown } from "@/components/dropdowns/cycle";
|
||||
// hooks
|
||||
import { captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { useIssuesStore } from "@/hooks/use-issue-layout-store";
|
||||
|
||||
type Props = {
|
||||
@@ -30,12 +28,6 @@ export const SpreadsheetCycleColumn = observer(function SpreadsheetCycleColumn(p
|
||||
if (!workspaceSlug || !issue || !issue.project_id || issue.cycle_id === cycleId) return;
|
||||
if (cycleId) await addCycleToIssue(workspaceSlug.toString(), issue.project_id, cycleId, issue.id);
|
||||
else await removeCycleFromIssue(workspaceSlug.toString(), issue.project_id, issue.id);
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: {
|
||||
id: issue.id,
|
||||
},
|
||||
});
|
||||
},
|
||||
[workspaceSlug, issue, addCycleToIssue, removeCycleFromIssue]
|
||||
);
|
||||
|
||||
+1
-11
@@ -1,15 +1,12 @@
|
||||
import React, { useCallback } from "react";
|
||||
import { useCallback } from "react";
|
||||
import { xor } from "lodash-es";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// types
|
||||
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||
import type { TIssue } from "@plane/types";
|
||||
// components
|
||||
import { ModuleDropdown } from "@/components/dropdowns/module/dropdown";
|
||||
// constants
|
||||
// hooks
|
||||
import { captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { useIssuesStore } from "@/hooks/use-issue-layout-store";
|
||||
|
||||
type Props = {
|
||||
@@ -39,13 +36,6 @@ export const SpreadsheetModuleColumn = observer(function SpreadsheetModuleColumn
|
||||
else modulesToAdd.push(moduleId);
|
||||
}
|
||||
changeModulesInIssue(workspaceSlug.toString(), issue.project_id, issue.id, modulesToAdd, modulesToRemove);
|
||||
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: {
|
||||
id: issue.id,
|
||||
},
|
||||
});
|
||||
},
|
||||
[workspaceSlug, issue, changeModulesInIssue]
|
||||
);
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { useRef } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// types
|
||||
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||
import type { IIssueDisplayProperties, TIssue } from "@plane/types";
|
||||
// hooks
|
||||
import { captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
// components
|
||||
import { SPREADSHEET_COLUMNS } from "@/plane-web/components/issues/issue-layouts/utils";
|
||||
import { shouldRenderColumn } from "@/plane-web/helpers/issue-filter.helper";
|
||||
@@ -30,6 +27,10 @@ export const IssueColumn = observer(function IssueColumn(props: Props) {
|
||||
|
||||
if (!Column) return null;
|
||||
|
||||
const handleUpdateIssue = async (issue: TIssue, data: Partial<TIssue>) => {
|
||||
if (updateIssue) await updateIssue(issue.project_id, issue.id, data);
|
||||
};
|
||||
|
||||
return (
|
||||
<WithDisplayPropertiesHOC
|
||||
displayProperties={displayProperties}
|
||||
@@ -43,17 +44,7 @@ export const IssueColumn = observer(function IssueColumn(props: Props) {
|
||||
>
|
||||
<Column
|
||||
issue={issueDetail}
|
||||
onChange={(issue: TIssue, data: Partial<TIssue>) =>
|
||||
updateIssue &&
|
||||
updateIssue(issue.project_id, issue.id, data).then(() => {
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: {
|
||||
id: issue.id,
|
||||
},
|
||||
});
|
||||
})
|
||||
}
|
||||
onChange={handleUpdateIssue}
|
||||
disabled={disableUserActions}
|
||||
onClose={() => tableCellRef?.current?.focus()}
|
||||
/>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { xor } from "lodash-es";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||
// Plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
@@ -10,7 +9,6 @@ import type { TBaseIssue, TIssue } from "@plane/types";
|
||||
import { EIssuesStoreType } from "@plane/types";
|
||||
import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
|
||||
// hooks
|
||||
import { captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||
import { useIssueModal } from "@/hooks/context/use-issue-modal";
|
||||
import { useCycle } from "@/hooks/store/use-cycle";
|
||||
import { useIssueDetail } from "@/hooks/store/use-issue-detail";
|
||||
@@ -241,10 +239,6 @@ export const CreateUpdateIssueModalBase = observer(function CreateUpdateIssueMod
|
||||
/>
|
||||
),
|
||||
});
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.create,
|
||||
payload: { id: response.id },
|
||||
});
|
||||
if (!createMore) handleClose();
|
||||
if (createMore && issueTitleRef) issueTitleRef?.current?.focus();
|
||||
setDescription("<p></p>");
|
||||
@@ -256,11 +250,6 @@ export const CreateUpdateIssueModalBase = observer(function CreateUpdateIssueMod
|
||||
title: t("error"),
|
||||
message: error?.error ?? t(is_draft_issue ? "draft_creation_failed" : "issue_creation_failed"),
|
||||
});
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.create,
|
||||
payload: { id: payload.id },
|
||||
error: error as Error,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
@@ -328,10 +317,6 @@ export const CreateUpdateIssueModalBase = observer(function CreateUpdateIssueMod
|
||||
/>
|
||||
) : undefined,
|
||||
});
|
||||
captureSuccess({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: data.id },
|
||||
});
|
||||
handleClose();
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
@@ -340,11 +325,6 @@ export const CreateUpdateIssueModalBase = observer(function CreateUpdateIssueMod
|
||||
title: t("error"),
|
||||
message: error?.error ?? t("issue_could_not_be_updated"),
|
||||
});
|
||||
captureError({
|
||||
eventName: WORK_ITEM_TRACKER_EVENTS.update,
|
||||
payload: { id: data.id },
|
||||
error: error as Error,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user