Compare commits

..
286 changed files with 3719 additions and 8960 deletions
-18
View File
@@ -15,22 +15,6 @@ Without said minimal reproduction, we won't be able to investigate all [issues](
You can open a new issue with this [issue form](https://github.com/makeplane/plane/issues/new).
### Naming conventions for issues
When opening a new issue, please use a clear and concise title that follows this format:
- For bugs: `🐛 Bug: [short description]`
- For features: `🚀 Feature: [short description]`
- For improvements: `🛠️ Improvement: [short description]`
- For documentation: `📘 Docs: [short description]`
**Examples:**
- `🐛 Bug: API token expiry time not saving correctly`
- `📘 Docs: Clarify RAM requirement for local setup`
- `🚀 Feature: Allow custom time selection for token expiration`
This helps us triage and manage issues more efficiently.
## Projects setup and Architecture
### Requirements
@@ -39,8 +23,6 @@ This helps us triage and manage issues more efficiently.
- Python version 3.8+
- Postgres version v14
- Redis version v6.2.7
- **Memory**: Minimum **12 GB RAM** recommended
> ⚠️ Running the project on a system with only 8 GB RAM may lead to setup failures or memory crashes (especially during Docker container build/start or dependency install). Use cloud environments like GitHub Codespaces or upgrade local RAM if possible.
### Setup the project
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "admin",
"description": "Admin UI for Plane",
"version": "0.26.0",
"version": "0.25.3",
"license": "AGPL-3.0",
"private": true,
"scripts": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "plane-api",
"version": "0.26.0",
"version": "0.25.3",
"license": "AGPL-3.0",
"private": true,
"description": "API server powering Plane's backend"
+1 -9
View File
@@ -39,15 +39,7 @@ class CycleSerializer(BaseSerializer):
data.get("start_date", None) is not None
and data.get("end_date", None) is not None
):
project_id = self.initial_data.get("project_id") or (
self.instance.project_id
if self.instance and hasattr(self.instance, "project_id")
else None
)
if not project_id:
raise serializers.ValidationError("Project ID is required")
project_id = self.initial_data.get("project_id") or self.instance.project_id
is_start_date_end_date_equal = (
True
if str(data.get("start_date")) == str(data.get("end_date"))
+14 -34
View File
@@ -141,10 +141,8 @@ class CycleAPIEndpoint(BaseAPIView):
if pk:
queryset = self.get_queryset().filter(archived_at__isnull=True).get(pk=pk)
data = CycleSerializer(
queryset,
fields=self.fields,
expand=self.expand,
context={"project": project},
queryset, fields=self.fields,
expand=self.expand, context={"project": project}
).data
return Response(data, status=status.HTTP_200_OK)
queryset = self.get_queryset().filter(archived_at__isnull=True)
@@ -156,11 +154,8 @@ class CycleAPIEndpoint(BaseAPIView):
start_date__lte=timezone.now(), end_date__gte=timezone.now()
)
data = CycleSerializer(
queryset,
many=True,
fields=self.fields,
expand=self.expand,
context={"project": project},
queryset, many=True, fields=self.fields,
expand=self.expand, context={"project": project}
).data
return Response(data, status=status.HTTP_200_OK)
@@ -171,11 +166,8 @@ class CycleAPIEndpoint(BaseAPIView):
request=request,
queryset=(queryset),
on_results=lambda cycles: CycleSerializer(
cycles,
many=True,
fields=self.fields,
expand=self.expand,
context={"project": project},
cycles, many=True, fields=self.fields,
expand=self.expand, context={"project": project}
).data,
)
@@ -186,11 +178,8 @@ class CycleAPIEndpoint(BaseAPIView):
request=request,
queryset=(queryset),
on_results=lambda cycles: CycleSerializer(
cycles,
many=True,
fields=self.fields,
expand=self.expand,
context={"project": project},
cycles, many=True, fields=self.fields,
expand=self.expand, context={"project": project}
).data,
)
@@ -201,11 +190,8 @@ class CycleAPIEndpoint(BaseAPIView):
request=request,
queryset=(queryset),
on_results=lambda cycles: CycleSerializer(
cycles,
many=True,
fields=self.fields,
expand=self.expand,
context={"project": project},
cycles, many=True, fields=self.fields,
expand=self.expand, context={"project": project}
).data,
)
@@ -218,22 +204,16 @@ class CycleAPIEndpoint(BaseAPIView):
request=request,
queryset=(queryset),
on_results=lambda cycles: CycleSerializer(
cycles,
many=True,
fields=self.fields,
expand=self.expand,
context={"project": project},
cycles, many=True, fields=self.fields,
expand=self.expand, context={"project": project}
).data,
)
return self.paginate(
request=request,
queryset=(queryset),
on_results=lambda cycles: CycleSerializer(
cycles,
many=True,
fields=self.fields,
expand=self.expand,
context={"project": project},
cycles, many=True, fields=self.fields,
expand=self.expand, context={"project": project}
).data,
)
+1 -2
View File
@@ -20,7 +20,6 @@ from plane.bgtasks.issue_activities_task import issue_activity
from plane.db.models import Intake, IntakeIssue, Issue, Project, ProjectMember, State
from plane.utils.host import base_host
from .base import BaseAPIView
from plane.db.models.intake import SourceType
class IntakeIssueAPIEndpoint(BaseAPIView):
@@ -126,7 +125,7 @@ class IntakeIssueAPIEndpoint(BaseAPIView):
intake_id=intake.id,
project_id=project_id,
issue=issue,
source=SourceType.IN_APP,
source=request.data.get("source", "IN-APP"),
)
# Create an Issue Activity
issue_activity.delay(
-12
View File
@@ -57,7 +57,6 @@ from plane.settings.storage import S3Storage
from plane.bgtasks.storage_metadata_task import get_asset_object_metadata
from .base import BaseAPIView
from plane.utils.host import base_host
from plane.bgtasks.webhook_task import model_activity
class WorkspaceIssueAPIEndpoint(BaseAPIView):
@@ -323,17 +322,6 @@ class IssueAPIEndpoint(BaseAPIView):
current_instance=None,
epoch=int(timezone.now().timestamp()),
)
# Send the model activity
model_activity.delay(
model_name="issue",
model_id=str(serializer.data["id"]),
requested_data=request.data,
current_instance=None,
actor_id=request.user.id,
slug=slug,
origin=base_host(request=request, is_app=True),
)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
+4 -5
View File
@@ -32,7 +32,6 @@ from plane.bgtasks.webhook_task import model_activity, webhook_activity
from .base import BaseAPIView
from plane.utils.host import base_host
class ProjectAPIEndpoint(BaseAPIView):
"""Project Endpoints to create, update, list, retrieve and delete endpoint"""
@@ -239,7 +238,7 @@ class ProjectAPIEndpoint(BaseAPIView):
if "already exists" in str(e):
return Response(
{"name": "The project name is already taken"},
status=status.HTTP_409_CONFLICT,
status=status.HTTP_410_GONE,
)
except Workspace.DoesNotExist:
return Response(
@@ -248,7 +247,7 @@ class ProjectAPIEndpoint(BaseAPIView):
except ValidationError:
return Response(
{"identifier": "The project identifier is already taken"},
status=status.HTTP_409_CONFLICT,
status=status.HTTP_410_GONE,
)
def patch(self, request, slug, pk):
@@ -306,7 +305,7 @@ class ProjectAPIEndpoint(BaseAPIView):
if "already exists" in str(e):
return Response(
{"name": "The project name is already taken"},
status=status.HTTP_409_CONFLICT,
status=status.HTTP_410_GONE,
)
except (Project.DoesNotExist, Workspace.DoesNotExist):
return Response(
@@ -315,7 +314,7 @@ class ProjectAPIEndpoint(BaseAPIView):
except ValidationError:
return Response(
{"identifier": "The project identifier is already taken"},
status=status.HTTP_409_CONFLICT,
status=status.HTTP_410_GONE,
)
def delete(self, request, slug, pk):
+5 -1
View File
@@ -115,7 +115,11 @@ from .intake import (
from .analytic import AnalyticViewSerializer
from .notification import NotificationSerializer, UserNotificationPreferenceSerializer
from .notification import (
NotificationSerializer,
UserNotificationPreferenceSerializer,
WorkspaceUserNotificationPreferenceSerializer
)
from .exporter import ExporterHistorySerializer
-9
View File
@@ -1,7 +1,5 @@
from .base import BaseSerializer
from plane.db.models import APIToken, APIActivityLog
from rest_framework import serializers
from django.utils import timezone
class APITokenSerializer(BaseSerializer):
@@ -19,17 +17,10 @@ class APITokenSerializer(BaseSerializer):
class APITokenReadSerializer(BaseSerializer):
is_active = serializers.SerializerMethodField()
class Meta:
model = APIToken
exclude = ("token",)
def get_is_active(self, obj: APIToken) -> bool:
if obj.expired_at is None:
return True
return timezone.now() < obj.expired_at
class APIActivityLogSerializer(BaseSerializer):
class Meta:
+2 -24
View File
@@ -352,19 +352,8 @@ class IssueRelationSerializer(BaseSerializer):
"state_id",
"priority",
"assignee_ids",
"created_by",
"created_at",
"updated_at",
"updated_by",
]
read_only_fields = [
"workspace",
"project",
"created_by",
"created_at",
"updated_by",
"updated_at",
]
read_only_fields = ["workspace", "project"]
class RelatedIssueSerializer(BaseSerializer):
@@ -394,19 +383,8 @@ class RelatedIssueSerializer(BaseSerializer):
"state_id",
"priority",
"assignee_ids",
"created_by",
"created_at",
"updated_by",
"updated_at",
]
read_only_fields = [
"workspace",
"project",
"created_by",
"created_at",
"updated_by",
"updated_at",
]
read_only_fields = ["workspace", "project"]
class IssueAssigneeSerializer(BaseSerializer):
@@ -1,7 +1,7 @@
# Module imports
from .base import BaseSerializer
from .user import UserLiteSerializer
from plane.db.models import Notification, UserNotificationPreference
from plane.db.models import Notification, UserNotificationPreference, WorkspaceUserNotificationPreference
# Third Party imports
from rest_framework import serializers
@@ -22,3 +22,8 @@ class UserNotificationPreferenceSerializer(BaseSerializer):
class Meta:
model = UserNotificationPreference
fields = "__all__"
class WorkspaceUserNotificationPreferenceSerializer(BaseSerializer):
class Meta:
model = WorkspaceUserNotificationPreference
fields = "__all__"
+11
View File
@@ -6,6 +6,7 @@ from plane.app.views import (
UnreadNotificationEndpoint,
MarkAllReadNotificationViewSet,
UserNotificationPreferenceEndpoint,
WorkspaceUserNotificationPreferenceEndpoint,
)
@@ -47,4 +48,14 @@ urlpatterns = [
UserNotificationPreferenceEndpoint.as_view(),
name="user-notification-preferences",
),
path(
"workspaces/<str:slug>/user-notification-preferences/<str:transport>/",
WorkspaceUserNotificationPreferenceEndpoint.as_view(),
name="workspace-user-notification-preference",
),
path(
"workspaces/<str:slug>/user-notification-preferences/",
WorkspaceUserNotificationPreferenceEndpoint.as_view(),
name="workspace-user-notification-preference",
),
]
+1
View File
@@ -203,6 +203,7 @@ from .notification.base import (
NotificationViewSet,
UnreadNotificationEndpoint,
UserNotificationPreferenceEndpoint,
WorkspaceUserNotificationPreferenceEndpoint,
)
from .exporter.base import ExportIssuesEndpoint
+4 -4
View File
@@ -137,7 +137,7 @@ class UserAssetsV2Endpoint(BaseAPIView):
if type not in allowed_types:
return Response(
{
"error": "Invalid file type. Only JPEG, PNG, WebP, JPG and GIF files are allowed.",
"error": "Invalid file type. Only JPEG and PNG files are allowed.",
"status": False,
},
status=status.HTTP_400_BAD_REQUEST,
@@ -351,7 +351,7 @@ class WorkspaceFileAssetEndpoint(BaseAPIView):
if type not in allowed_types:
return Response(
{
"error": "Invalid file type. Only JPEG, PNG, WebP, JPG and GIF files are allowed.",
"error": "Invalid file type. Only JPEG and PNG files are allowed.",
"status": False,
},
status=status.HTTP_400_BAD_REQUEST,
@@ -552,7 +552,7 @@ class ProjectAssetEndpoint(BaseAPIView):
if type not in allowed_types:
return Response(
{
"error": "Invalid file type. Only JPEG, PNG, WebP, JPG and GIF files are allowed.",
"error": "Invalid file type. Only JPEG and PNG files are allowed.",
"status": False,
},
status=status.HTTP_400_BAD_REQUEST,
@@ -683,7 +683,7 @@ class ProjectBulkAssetEndpoint(BaseAPIView):
# For some cases, the bulk api is called after the issue is deleted creating
# an integrity error
try:
assets.update(issue_id=entity_id, project_id=project_id)
assets.update(issue_id=entity_id)
except IntegrityError:
pass
+3 -2
View File
@@ -44,7 +44,6 @@ from plane.app.views.base import BaseAPIView
from plane.utils.timezone_converter import user_timezone_converter
from plane.utils.global_paginator import paginate
from plane.utils.host import base_host
from plane.db.models.intake import SourceType
class IntakeViewSet(BaseViewSet):
@@ -279,7 +278,7 @@ class IntakeIssueViewSet(BaseViewSet):
intake_id=intake_id.id,
project_id=project_id,
issue_id=serializer.data["id"],
source=SourceType.IN_APP,
source=request.data.get("source", "IN-APP"),
)
# Create an Issue Activity
issue_activity.delay(
@@ -409,6 +408,7 @@ class IntakeIssueViewSet(BaseViewSet):
)
if issue_serializer.is_valid():
# Log all the updates
requested_data = json.dumps(issue_data, cls=DjangoJSONEncoder)
if issue is not None:
@@ -607,6 +607,7 @@ class IntakeIssueViewSet(BaseViewSet):
class IntakeWorkItemDescriptionVersionEndpoint(BaseAPIView):
def process_paginated_result(self, fields, results, timezone):
paginated_data = results.values(*fields)
@@ -9,6 +9,7 @@ from rest_framework.response import Response
from plane.app.serializers import (
NotificationSerializer,
UserNotificationPreferenceSerializer,
WorkspaceUserNotificationPreferenceSerializer
)
from plane.db.models import (
Issue,
@@ -17,6 +18,9 @@ from plane.db.models import (
Notification,
UserNotificationPreference,
WorkspaceMember,
Workspace,
WorkspaceUserNotificationPreference,
NotificationTransportChoices
)
from plane.utils.paginator import BasePaginator
from plane.app.permissions import allow_permission, ROLE
@@ -360,3 +364,81 @@ class UserNotificationPreferenceEndpoint(BaseAPIView):
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class WorkspaceUserNotificationPreferenceEndpoint(BaseAPIView):
model = WorkspaceUserNotificationPreference
serializer_class = WorkspaceUserNotificationPreferenceSerializer
@allow_permission(
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE"
)
def get(self, request, slug):
workspace = Workspace.objects.get(slug=slug)
get_notification_preferences = (
WorkspaceUserNotificationPreference.objects.filter(
workspace=workspace, user=request.user
)
)
create_transports = []
transports = [
transport
for transport, _ in NotificationTransportChoices.choices
]
for transport in transports:
if transport not in get_notification_preferences.values_list(
"transport", flat=True
):
create_transports.append(transport)
notification_preferences = (
WorkspaceUserNotificationPreference.objects.bulk_create(
[
WorkspaceUserNotificationPreference(
workspace=workspace,
user=request.user,
transport=transport,
)
for transport in create_transports
]
)
)
notification_preferences = WorkspaceUserNotificationPreference.objects.filter(
workspace=workspace, user=request.user
)
return Response(
WorkspaceUserNotificationPreferenceSerializer(
notification_preferences, many=True
).data,
status=status.HTTP_200_OK,
)
@allow_permission(
allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE"
)
def patch(self, request, slug, transport):
notification_preference = WorkspaceUserNotificationPreference.objects.filter(
transport=transport, workspace__slug=slug, user=request.user
).first()
if notification_preference:
serializer = WorkspaceUserNotificationPreferenceSerializer(
notification_preference, data=request.data, partial=True
)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
return Response(
{"detail": "Workspace notification preference not found"},
status=status.HTTP_404_NOT_FOUND,
)
+4 -5
View File
@@ -41,7 +41,6 @@ from plane.bgtasks.recent_visited_task import recent_visited_task
from plane.utils.exception_logger import log_exception
from plane.utils.host import base_host
class ProjectViewSet(BaseViewSet):
serializer_class = ProjectListSerializer
model = Project
@@ -342,7 +341,7 @@ class ProjectViewSet(BaseViewSet):
if "already exists" in str(e):
return Response(
{"name": "The project name is already taken"},
status=status.HTTP_409_CONFLICT,
status=status.HTTP_410_GONE,
)
except Workspace.DoesNotExist:
return Response(
@@ -351,7 +350,7 @@ class ProjectViewSet(BaseViewSet):
except serializers.ValidationError:
return Response(
{"identifier": "The project identifier is already taken"},
status=status.HTTP_409_CONFLICT,
status=status.HTTP_410_GONE,
)
def partial_update(self, request, slug, pk=None):
@@ -420,7 +419,7 @@ class ProjectViewSet(BaseViewSet):
if "already exists" in str(e):
return Response(
{"name": "The project name is already taken"},
status=status.HTTP_409_CONFLICT,
status=status.HTTP_410_GONE,
)
except (Project.DoesNotExist, Workspace.DoesNotExist):
return Response(
@@ -429,7 +428,7 @@ class ProjectViewSet(BaseViewSet):
except serializers.ValidationError:
return Response(
{"identifier": "The project identifier is already taken"},
status=status.HTTP_409_CONFLICT,
status=status.HTTP_410_GONE,
)
def destroy(self, request, slug, pk):
+1 -1
View File
@@ -29,7 +29,7 @@ class WebhookEndpoint(BaseAPIView):
if "already exists" in str(e):
return Response(
{"error": "URL already exists for the workspace"},
status=status.HTTP_409_CONFLICT,
status=status.HTTP_410_GONE,
)
raise IntegrityError
+6 -7
View File
@@ -119,9 +119,7 @@ class WorkSpaceViewSet(BaseViewSet):
)
# Get total members and role
total_members = WorkspaceMember.objects.filter(
workspace_id=serializer.data["id"]
).count()
total_members=WorkspaceMember.objects.filter(workspace_id=serializer.data["id"]).count()
data = serializer.data
data["total_members"] = total_members
data["role"] = 20
@@ -136,7 +134,7 @@ class WorkSpaceViewSet(BaseViewSet):
if "already exists" in str(e):
return Response(
{"slug": "The workspace with the slug already exists"},
status=status.HTTP_409_CONFLICT,
status=status.HTTP_410_GONE,
)
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE")
@@ -169,9 +167,10 @@ class UserWorkSpacesEndpoint(BaseAPIView):
.values("count")
)
role = WorkspaceMember.objects.filter(
workspace=OuterRef("id"), member=request.user, is_active=True
).values("role")
role = (
WorkspaceMember.objects.filter(workspace=OuterRef("id"), member=request.user, is_active=True)
.values("role")
)
workspace = (
Workspace.objects.prefetch_related(
+32 -1
View File
@@ -2,7 +2,7 @@
from ..base import BaseAPIView
from plane.db.models.workspace import WorkspaceHomePreference
from plane.app.permissions import allow_permission, ROLE
from plane.db.models import Workspace
from plane.db.models import Workspace, WorkspaceUserNotificationPreference, NotificationTransportChoices
from plane.app.serializers.workspace import WorkspaceHomePreferenceSerializer
# Third party imports
@@ -59,6 +59,37 @@ class WorkspaceHomePreferenceViewSet(BaseAPIView):
user=request.user, workspace_id=workspace.id
)
# Notification preference get or create
workspace = Workspace.objects.get(slug=slug)
get_notification_preferences = (
WorkspaceUserNotificationPreference.objects.filter(
workspace=workspace, user=request.user
)
)
create_transports = []
transports = [
transport
for transport, _ in NotificationTransportChoices.choices
]
for transport in transports:
if transport not in get_notification_preferences.values_list(
"transport", flat=True
):
create_transports.append(transport)
_ = WorkspaceUserNotificationPreference.objects.bulk_create(
[
WorkspaceUserNotificationPreference(
workspace=workspace, user=request.user, transport=transport
)
for transport in create_transports
]
)
return Response(
preference.values("key", "is_enabled", "config", "sort_order"),
status=status.HTTP_200_OK,
@@ -307,10 +307,6 @@ def track_labels(
# Set of newly added labels
for added_label in added_labels:
# validate uuids
if not is_valid_uuid(added_label):
continue
label = Label.objects.get(pk=added_label)
issue_activities.append(
IssueActivity(
@@ -331,10 +327,6 @@ def track_labels(
# Set of dropped labels
for dropped_label in dropped_labels:
# validate uuids
if not is_valid_uuid(dropped_label):
continue
label = Label.objects.get(pk=dropped_label)
issue_activities.append(
IssueActivity(
@@ -381,10 +373,6 @@ def track_assignees(
bulk_subscribers = []
for added_asignee in added_assignees:
# validate uuids
if not is_valid_uuid(added_asignee):
continue
assignee = User.objects.get(pk=added_asignee)
issue_activities.append(
IssueActivity(
@@ -418,10 +406,6 @@ def track_assignees(
)
for dropped_assignee in dropped_assginees:
# validate uuids
if not is_valid_uuid(dropped_assignee):
continue
assignee = User.objects.get(pk=dropped_assignee)
issue_activities.append(
IssueActivity(
@@ -482,7 +466,7 @@ def track_estimate_points(
),
old_value=old_estimate.value if old_estimate else None,
new_value=new_estimate.value if new_estimate else None,
field="estimate_" + new_estimate.estimate.type,
field="estimate_point",
project_id=project_id,
workspace_id=workspace_id,
comment="updated the estimate point to ",
File diff suppressed because it is too large Load Diff
+1 -31
View File
@@ -1,15 +1,7 @@
# Python imports
import os
import logging
# Third party imports
from celery import Celery
from pythonjsonlogger.jsonlogger import JsonFormatter
from celery.signals import after_setup_logger, after_setup_task_logger
from celery.schedules import crontab
# Module imports
from plane.settings.redis import redis_instance
from celery.schedules import crontab
# Set the default Django settings module for the 'celery' program.
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "plane.settings.production")
@@ -55,28 +47,6 @@ app.conf.beat_schedule = {
},
}
# Setup logging
@after_setup_logger.connect
def setup_loggers(logger, *args, **kwargs):
formatter = JsonFormatter(
'"%(levelname)s %(asctime)s %(module)s %(name)s %(message)s'
)
handler = logging.StreamHandler()
handler.setFormatter(fmt=formatter)
logger.addHandler(handler)
@after_setup_task_logger.connect
def setup_task_loggers(logger, *args, **kwargs):
formatter = JsonFormatter(
'"%(levelname)s %(asctime)s %(module)s %(name)s %(message)s'
)
handler = logging.StreamHandler()
handler.setFormatter(fmt=formatter)
logger.addHandler(handler)
# Load task modules from all registered Django app configs.
app.autodiscover_tasks()
@@ -1,31 +0,0 @@
# Generated by Django 4.2.17 on 2025-03-04 19:29
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
("db", "0092_alter_deprecateddashboardwidget_unique_together_and_more"),
]
operations = [
migrations.AddField(
model_name="page",
name="moved_to_page",
field=models.UUIDField(blank=True, null=True),
),
migrations.AddField(
model_name="page",
name="moved_to_project",
field=models.UUIDField(blank=True, null=True),
),
migrations.AddField(
model_name="pageversion",
name="sub_pages_data",
field=models.JSONField(blank=True, default=dict),
),
]
+8 -2
View File
@@ -44,7 +44,13 @@ from .issue import (
IssueDescriptionVersion,
)
from .module import Module, ModuleIssue, ModuleLink, ModuleMember, ModuleUserProperties
from .notification import EmailNotificationLog, Notification, UserNotificationPreference
from .notification import (
EmailNotificationLog,
Notification,
UserNotificationPreference,
NotificationTransportChoices,
WorkspaceUserNotificationPreference
)
from .page import Page, PageLabel, PageLog, ProjectPage, PageVersion
from .project import (
Project,
@@ -82,4 +88,4 @@ from .label import Label
from .device import Device, DeviceSession
from .sticky import Sticky
from .sticky import Sticky
-4
View File
@@ -31,10 +31,6 @@ class Intake(ProjectBaseModel):
ordering = ("name",)
class SourceType(models.TextChoices):
IN_APP = "IN_APP"
class IntakeIssue(ProjectBaseModel):
intake = models.ForeignKey(
"db.Intake", related_name="issue_intake", on_delete=models.CASCADE
+56 -1
View File
@@ -93,7 +93,6 @@ class UserNotificationPreference(BaseModel):
"""Return the user"""
return f"<{self.user}>"
class EmailNotificationLog(BaseModel):
# receiver
receiver = models.ForeignKey(
@@ -123,3 +122,59 @@ class EmailNotificationLog(BaseModel):
verbose_name_plural = "Email Notification Logs"
db_table = "email_notification_logs"
ordering = ("-created_at",)
class WorkspaceUserNotificationPreference(BaseModel):
# user it is related to
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="user_workspace_notification_preferences",
)
# workspace if it is applicable
workspace = models.ForeignKey(
"db.Workspace",
on_delete=models.CASCADE,
related_name="workspace_user_notification_preferences",
)
# project
project = models.ForeignKey(
"db.Project",
on_delete=models.CASCADE,
related_name="project_user_notification_preferences",
null=True,
)
transport = models.CharField(max_length=50, default="EMAIL")
# task updates
property_change = models.BooleanField(default=False)
state_change = models.BooleanField(default=False)
priority = models.BooleanField(default=False)
assignee = models.BooleanField(default=False)
start_due_date = models.BooleanField(default=False)
# comments fields
comment = models.BooleanField(default=False)
mention = models.BooleanField(default=False)
comment_reactions = models.BooleanField(default=False)
class Meta:
unique_together = ["workspace", "user", "transport", "deleted_at"]
constraints = [
models.UniqueConstraint(
fields=["workspace", "user", "transport"],
condition=models.Q(deleted_at__isnull=True),
name="notification_preferences_unique_workspace_user_transport_when_deleted_at_null",
)
]
verbose_name = "Workspace User Notification Preference"
verbose_name_plural = "Workspace User Notification Preferences"
db_table = "workspace_user_notification_preferences"
ordering = ("-created_at",)
def __str__(self):
"""Return the user"""
return f"<{self.user}>"
class NotificationTransportChoices(models.TextChoices):
EMAIL = "EMAIL", "Email"
IN_APP = "IN_APP", "In App"
-3
View File
@@ -50,8 +50,6 @@ class Page(BaseModel):
projects = models.ManyToManyField(
"db.Project", related_name="pages", through="db.ProjectPage"
)
moved_to_page = models.UUIDField(null=True, blank=True)
moved_to_project = models.UUIDField(null=True, blank=True)
class Meta:
verbose_name = "Page"
@@ -174,7 +172,6 @@ class PageVersion(BaseModel):
description_html = models.TextField(blank=True, default="<p></p>")
description_stripped = models.TextField(blank=True, null=True)
description_json = models.JSONField(default=dict, blank=True)
sub_pages_data = models.JSONField(default=dict, blank=True)
class Meta:
verbose_name = "Page Version"
@@ -109,5 +109,5 @@ class InstanceWorkSpaceEndpoint(BaseAPIView):
if "already exists" in str(e):
return Response(
{"slug": "The workspace with the slug already exists"},
status=status.HTTP_409_CONFLICT,
status=status.HTTP_410_GONE,
)
@@ -0,0 +1,40 @@
# Module imports
from plane.db.models import APIActivityLog
from plane.utils.ip_address import get_client_ip
class APITokenLogMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request_body = request.body
response = self.get_response(request)
self.process_request(request, response, request_body)
return response
def process_request(self, request, response, request_body):
api_key_header = "X-Api-Key"
api_key = request.headers.get(api_key_header)
# If the API key is present, log the request
if api_key:
try:
APIActivityLog.objects.create(
token_identifier=api_key,
path=request.path,
method=request.method,
query_params=request.META.get("QUERY_STRING", ""),
headers=str(request.headers),
body=(request_body.decode("utf-8") if request_body else None),
response_body=(
response.content.decode("utf-8") if response.content else None
),
response_code=response.status_code,
ip_address=get_client_ip(request=request),
user_agent=request.META.get("HTTP_USER_AGENT", None),
)
except Exception as e:
print(e)
# If the token does not exist, you can decide whether to log this as an invalid attempt
return None
-111
View File
@@ -1,111 +0,0 @@
# Python imports
import logging
import time
# Django imports
from django.http import HttpRequest
# Third party imports
from rest_framework.request import Request
# Module imports
from plane.utils.ip_address import get_client_ip
from plane.db.models import APIActivityLog
api_logger = logging.getLogger("plane.api.request")
class RequestLoggerMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def _should_log_route(self, request: Request | HttpRequest) -> bool:
"""
Determines whether a route should be logged based on the request and status code.
"""
# Don't log health checks
if request.path == "/" and request.method == "GET":
return False
return True
def __call__(self, request):
# get the start time
start_time = time.time()
# Get the response
response = self.get_response(request)
# calculate the duration
duration = time.time() - start_time
# Check if logging is required
log_true = self._should_log_route(request=request)
# If logging is not required, return the response
if not log_true:
return response
user_id = (
request.user.id
if getattr(request, "user")
and getattr(request.user, "is_authenticated", False)
else None
)
user_agent = request.META.get("HTTP_USER_AGENT", "")
# Log the request information
api_logger.info(
f"{request.method} {request.get_full_path()} {response.status_code}",
extra={
"path": request.path,
"method": request.method,
"status_code": response.status_code,
"duration_ms": int(duration * 1000),
"remote_addr": get_client_ip(request),
"user_agent": user_agent,
"user_id": user_id,
},
)
# return the response
return response
class APITokenLogMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request_body = request.body
response = self.get_response(request)
self.process_request(request, response, request_body)
return response
def process_request(self, request, response, request_body):
api_key_header = "X-Api-Key"
api_key = request.headers.get(api_key_header)
# If the API key is present, log the request
if api_key:
try:
APIActivityLog.objects.create(
token_identifier=api_key,
path=request.path,
method=request.method,
query_params=request.META.get("QUERY_STRING", ""),
headers=str(request.headers),
body=(request_body.decode("utf-8") if request_body else None),
response_body=(
response.content.decode("utf-8") if response.content else None
),
response_code=response.status_code,
ip_address=get_client_ip(request=request),
user_agent=request.META.get("HTTP_USER_AGENT", None),
)
except Exception as e:
api_logger.exception(e)
# If the token does not exist, you can decide whether to log this as an invalid attempt
return None
+1 -6
View File
@@ -58,8 +58,7 @@ MIDDLEWARE = [
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"crum.CurrentRequestUserMiddleware",
"django.middleware.gzip.GZipMiddleware",
"plane.middleware.logger.APITokenLogMiddleware",
"plane.middleware.logger.RequestLoggerMiddleware",
"plane.middleware.api_log_middleware.APITokenLogMiddleware",
]
# Rest Framework settings
@@ -391,8 +390,4 @@ ATTACHMENT_MIME_TYPES = [
"text/xml",
"text/csv",
"application/xml",
# SQL
"application/x-sql",
# Gzip
"application/x-gzip",
]
+6 -21
View File
@@ -37,41 +37,26 @@ if not os.path.exists(LOG_DIR):
LOGGING = {
"version": 1,
"disable_existing_loggers": True,
"disable_existing_loggers": False,
"formatters": {
"verbose": {
"format": "{levelname} {asctime} {module} {process:d} {thread:d} {message}",
"style": "{",
},
"json": {
"()": "pythonjsonlogger.jsonlogger.JsonFormatter",
"fmt": "%(levelname)s %(asctime)s %(module)s %(name)s %(message)s",
},
}
},
"handlers": {
"console": {
"level": "DEBUG",
"class": "logging.StreamHandler",
"formatter": "json",
"formatter": "verbose",
}
},
"loggers": {
"plane.api.request": {
"level": "INFO",
"handlers": ["console"],
"propagate": False,
},
"plane.api": {"level": "INFO", "handlers": ["console"], "propagate": False},
"plane.worker": {"level": "INFO", "handlers": ["console"], "propagate": False},
"plane.exception": {
"level": "ERROR",
"handlers": ["console"],
"propagate": False,
},
"plane.external": {
"level": "INFO",
"django.request": {
"handlers": ["console"],
"level": "DEBUG",
"propagate": False,
},
"plane": {"handlers": ["console"], "level": "DEBUG", "propagate": False},
},
}
+9 -22
View File
@@ -26,10 +26,11 @@ if not os.path.exists(LOG_DIR):
# Logging configuration
LOGGING = {
"version": 1,
"disable_existing_loggers": True,
"disable_existing_loggers": False,
"formatters": {
"verbose": {
"format": "%(asctime)s [%(process)d] %(levelname)s %(name)s: %(message)s"
"format": "{levelname} {asctime} {module} {process:d} {thread:d} {message}",
"style": "{",
},
"json": {
"()": "pythonjsonlogger.jsonlogger.JsonFormatter",
@@ -39,7 +40,7 @@ LOGGING = {
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "json",
"formatter": "verbose",
"level": "INFO",
},
"file": {
@@ -58,30 +59,16 @@ LOGGING = {
},
},
"loggers": {
"plane.api.request": {
"level": "DEBUG" if DEBUG else "INFO",
"handlers": ["console"],
"django": {"handlers": ["console", "file"], "level": "INFO", "propagate": True},
"django.request": {
"handlers": ["console", "file"],
"level": "INFO",
"propagate": False,
},
"plane.api": {
"level": "DEBUG" if DEBUG else "INFO",
"handlers": ["console"],
"propagate": False,
},
"plane.worker": {
"level": "DEBUG" if DEBUG else "INFO",
"handlers": ["console"],
"propagate": False,
},
"plane.exception": {
"plane": {
"level": "DEBUG" if DEBUG else "ERROR",
"handlers": ["console", "file"],
"propagate": False,
},
"plane.external": {
"level": "INFO",
"handlers": ["console"],
"propagate": False,
},
},
}
+3 -25
View File
@@ -3,9 +3,6 @@ from django.contrib.postgres.aggregates import ArrayAgg
from django.contrib.postgres.fields import ArrayField
from django.db.models import Q, UUIDField, Value, F, Case, When, JSONField, CharField
from django.db.models.functions import Coalesce, JSONObject, Concat
from django.db.models import QuerySet
from typing import List, Optional, Dict, Any, Union
# Module imports
from plane.db.models import (
@@ -20,25 +17,13 @@ from plane.db.models import (
)
def issue_queryset_grouper(
queryset: QuerySet[Issue], group_by: Optional[str], sub_group_by: Optional[str]
) -> QuerySet[Issue]:
def issue_queryset_grouper(queryset, group_by, sub_group_by):
FIELD_MAPPER = {
"label_ids": "labels__id",
"assignee_ids": "assignees__id",
"module_ids": "issue_module__module_id",
}
GROUP_FILTER_MAPPER = {
"assignees__id": Q(issue_assignee__deleted_at__isnull=True),
"labels__id": Q(label_issue__deleted_at__isnull=True),
"issue_module__module_id": Q(issue_module__deleted_at__isnull=True),
}
for group_key in [group_by, sub_group_by]:
if group_key in GROUP_FILTER_MAPPER:
queryset = queryset.filter(GROUP_FILTER_MAPPER[group_key])
annotations_map = {
"assignee_ids": (
"assignees__id",
@@ -65,9 +50,7 @@ def issue_queryset_grouper(
return queryset.annotate(**default_annotations)
def issue_on_results(
issues: QuerySet[Issue], group_by: Optional[str], sub_group_by: Optional[str]
) -> List[Dict[str, Any]]:
def issue_on_results(issues, group_by, sub_group_by):
FIELD_MAPPER = {
"labels__id": "label_ids",
"assignees__id": "assignee_ids",
@@ -177,12 +160,7 @@ def issue_on_results(
return issues
def issue_group_values(
field: str,
slug: str,
project_id: Optional[str] = None,
filters: Dict[str, Any] = {},
) -> List[Union[str, Any]]:
def issue_group_values(field, slug, project_id=None, filters=dict):
if field == "state_id":
queryset = State.objects.filter(
is_triage=False, workspace__slug=slug
+1 -1
View File
@@ -96,7 +96,7 @@ class EntityAssetEndpoint(BaseAPIView):
if type not in allowed_types:
return Response(
{
"error": "Invalid file type. Only JPEG, PNG, WebP, JPG and GIF files are allowed.",
"error": "Invalid file type. Only JPEG and PNG files are allowed.",
"status": False,
},
status=status.HTTP_400_BAD_REQUEST,
+1 -1
View File
@@ -41,4 +41,4 @@ class WorkSpaceCreateReadUpdateDelete(AuthenticatedAPITest):
response = self.client.post(
url, {"name": "Plane", "slug": "pla-ne"}, format="json"
)
self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)
self.assertEqual(response.status_code, status.HTTP_410_GONE)
+2 -2
View File
@@ -8,8 +8,8 @@ from django.conf import settings
def log_exception(e):
# Log the error
logger = logging.getLogger("plane.exception")
logger.exception(e)
logger = logging.getLogger("plane")
logger.error(e)
if settings.DEBUG:
# Print the traceback if in debug mode
+31 -59
View File
@@ -1,7 +1,7 @@
# Django imports
from django.contrib.postgres.aggregates import ArrayAgg
from django.contrib.postgres.fields import ArrayField
from django.db.models import Q, UUIDField, Value, QuerySet
from django.db.models import Q, UUIDField, Value
from django.db.models.functions import Coalesce
# Module imports
@@ -15,31 +15,16 @@ from plane.db.models import (
State,
WorkspaceMember,
)
from typing import Optional, Dict, Tuple, Any, Union, List
def issue_queryset_grouper(
queryset: QuerySet[Issue],
group_by: Optional[str],
sub_group_by: Optional[str],
) -> QuerySet[Issue]:
FIELD_MAPPER: Dict[str, str] = {
def issue_queryset_grouper(queryset, group_by, sub_group_by):
FIELD_MAPPER = {
"label_ids": "labels__id",
"assignee_ids": "assignees__id",
"module_ids": "issue_module__module_id",
}
GROUP_FILTER_MAPPER: Dict[str, Q] = {
"assignees__id": Q(issue_assignee__deleted_at__isnull=True),
"labels__id": Q(label_issue__deleted_at__isnull=True),
"issue_module__module_id": Q(issue_module__deleted_at__isnull=True),
}
for group_key in [group_by, sub_group_by]:
if group_key in GROUP_FILTER_MAPPER:
queryset = queryset.filter(GROUP_FILTER_MAPPER[group_key])
annotations_map: Dict[str, Tuple[str, Q]] = {
annotations_map = {
"assignee_ids": (
"assignees__id",
~Q(assignees__id__isnull=True) & Q(issue_assignee__deleted_at__isnull=True),
@@ -57,8 +42,7 @@ def issue_queryset_grouper(
),
),
}
default_annotations: Dict[str, Any] = {
default_annotations = {
key: Coalesce(
ArrayAgg(field, distinct=True, filter=condition),
Value([], output_field=ArrayField(UUIDField())),
@@ -70,20 +54,16 @@ def issue_queryset_grouper(
return queryset.annotate(**default_annotations)
def issue_on_results(
issues: QuerySet[Issue],
group_by: Optional[str],
sub_group_by: Optional[str],
) -> List[Dict[str, Any]]:
FIELD_MAPPER: Dict[str, str] = {
def issue_on_results(issues, group_by, sub_group_by):
FIELD_MAPPER = {
"labels__id": "label_ids",
"assignees__id": "assignee_ids",
"issue_module__module_id": "module_ids",
}
original_list: List[str] = ["assignee_ids", "label_ids", "module_ids"]
original_list = ["assignee_ids", "label_ids", "module_ids"]
required_fields: List[str] = [
required_fields = [
"id",
"name",
"state_id",
@@ -118,72 +98,62 @@ def issue_on_results(
original_list.append(sub_group_by)
required_fields.extend(original_list)
return list(issues.values(*required_fields))
return issues.values(*required_fields)
def issue_group_values(
field: str,
slug: str,
project_id: Optional[str] = None,
filters: Dict[str, Any] = {},
) -> List[Union[str, Any]]:
def issue_group_values(field, slug, project_id=None, filters=dict):
if field == "state_id":
queryset = State.objects.filter(
is_triage=False, workspace__slug=slug
).values_list("id", flat=True)
if project_id:
return list(queryset.filter(project_id=project_id))
return list(queryset)
else:
return list(queryset)
if field == "labels__id":
queryset = Label.objects.filter(workspace__slug=slug).values_list(
"id", flat=True
)
if project_id:
return list(queryset.filter(project_id=project_id)) + ["None"]
return list(queryset) + ["None"]
else:
return list(queryset) + ["None"]
if field == "assignees__id":
if project_id:
return ProjectMember.objects.filter(
workspace__slug=slug, project_id=project_id, is_active=True
).values_list("member_id", flat=True)
else:
return list(
ProjectMember.objects.filter(
workspace__slug=slug, project_id=project_id, is_active=True
WorkspaceMember.objects.filter(
workspace__slug=slug, is_active=True
).values_list("member_id", flat=True)
)
return list(
WorkspaceMember.objects.filter(
workspace__slug=slug, is_active=True
).values_list("member_id", flat=True)
)
if field == "issue_module__module_id":
queryset = Module.objects.filter(workspace__slug=slug).values_list(
"id", flat=True
)
if project_id:
return list(queryset.filter(project_id=project_id)) + ["None"]
return list(queryset) + ["None"]
else:
return list(queryset) + ["None"]
if field == "cycle_id":
queryset = Cycle.objects.filter(workspace__slug=slug).values_list(
"id", flat=True
)
if project_id:
return list(queryset.filter(project_id=project_id)) + ["None"]
return list(queryset) + ["None"]
else:
return list(queryset) + ["None"]
if field == "project_id":
queryset = Project.objects.filter(workspace__slug=slug).values_list(
"id", flat=True
)
return list(queryset)
if field == "priority":
return ["low", "medium", "high", "urgent", "none"]
if field == "state__group":
return ["backlog", "unstarted", "started", "completed", "cancelled"]
if field == "target_date":
queryset = (
Issue.issue_objects.filter(workspace__slug=slug)
@@ -193,8 +163,8 @@ def issue_group_values(
)
if project_id:
return list(queryset.filter(project_id=project_id))
return list(queryset)
else:
return list(queryset)
if field == "start_date":
queryset = (
Issue.issue_objects.filter(workspace__slug=slug)
@@ -204,7 +174,8 @@ def issue_group_values(
)
if project_id:
return list(queryset.filter(project_id=project_id))
return list(queryset)
else:
return list(queryset)
if field == "created_by":
queryset = (
@@ -215,6 +186,7 @@ def issue_group_values(
)
if project_id:
return list(queryset.filter(project_id=project_id))
return list(queryset)
else:
return list(queryset)
return []
+1 -1
View File
@@ -43,7 +43,7 @@ scout-apm==3.1.0
# xlsx generation
openpyxl==3.1.2
# logging
python-json-logger==3.3.0
python-json-logger==2.0.7
# html parser
beautifulsoup4==4.12.3
# analytics
+9 -9
View File
@@ -54,7 +54,7 @@ x-app-env: &app-env
services:
web:
image: ${DOCKERHUB_USER:-artifacts.plane.so/makeplane}/plane-frontend:${APP_RELEASE:-stable}
image: ${DOCKERHUB_USER:-makeplane}/plane-frontend:${APP_RELEASE:-stable}
command: node web/server.js web
deploy:
replicas: ${WEB_REPLICAS:-1}
@@ -65,7 +65,7 @@ services:
- worker
space:
image: ${DOCKERHUB_USER:-artifacts.plane.so/makeplane}/plane-space:${APP_RELEASE:-stable}
image: ${DOCKERHUB_USER:-makeplane}/plane-space:${APP_RELEASE:-stable}
command: node space/server.js space
deploy:
replicas: ${SPACE_REPLICAS:-1}
@@ -77,7 +77,7 @@ services:
- web
admin:
image: ${DOCKERHUB_USER:-artifacts.plane.so/makeplane}/plane-admin:${APP_RELEASE:-stable}
image: ${DOCKERHUB_USER:-makeplane}/plane-admin:${APP_RELEASE:-stable}
command: node admin/server.js admin
deploy:
replicas: ${ADMIN_REPLICAS:-1}
@@ -88,7 +88,7 @@ services:
- web
live:
image: ${DOCKERHUB_USER:-artifacts.plane.so/makeplane}/plane-live:${APP_RELEASE:-stable}
image: ${DOCKERHUB_USER:-makeplane}/plane-live:${APP_RELEASE:-stable}
command: node live/dist/server.js live
environment:
<<: [*live-env]
@@ -101,7 +101,7 @@ services:
- web
api:
image: ${DOCKERHUB_USER:-artifacts.plane.so/makeplane}/plane-backend:${APP_RELEASE:-stable}
image: ${DOCKERHUB_USER:-makeplane}/plane-backend:${APP_RELEASE:-stable}
command: ./bin/docker-entrypoint-api.sh
deploy:
replicas: ${API_REPLICAS:-1}
@@ -117,7 +117,7 @@ services:
- plane-mq
worker:
image: ${DOCKERHUB_USER:-artifacts.plane.so/makeplane}/plane-backend:${APP_RELEASE:-stable}
image: ${DOCKERHUB_USER:-makeplane}/plane-backend:${APP_RELEASE:-stable}
command: ./bin/docker-entrypoint-worker.sh
deploy:
replicas: ${WORKER_REPLICAS:-1}
@@ -134,7 +134,7 @@ services:
- plane-mq
beat-worker:
image: ${DOCKERHUB_USER:-artifacts.plane.so/makeplane}/plane-backend:${APP_RELEASE:-stable}
image: ${DOCKERHUB_USER:-makeplane}/plane-backend:${APP_RELEASE:-stable}
command: ./bin/docker-entrypoint-beat.sh
deploy:
replicas: ${BEAT_WORKER_REPLICAS:-1}
@@ -151,7 +151,7 @@ services:
- plane-mq
migrator:
image: ${DOCKERHUB_USER:-artifacts.plane.so/makeplane}/plane-backend:${APP_RELEASE:-stable}
image: ${DOCKERHUB_USER:-makeplane}/plane-backend:${APP_RELEASE:-stable}
command: ./bin/docker-entrypoint-migrator.sh
deploy:
replicas: 1
@@ -213,7 +213,7 @@ services:
# Comment this if you already have a reverse proxy running
proxy:
image: ${DOCKERHUB_USER:-artifacts.plane.so/makeplane}/plane-proxy:${APP_RELEASE:-stable}
image: ${DOCKERHUB_USER:-makeplane}/plane-proxy:${APP_RELEASE:-stable}
ports:
- target: 80
published: ${NGINX_PORT:-80}
+1 -1
View File
@@ -60,4 +60,4 @@ GUNICORN_WORKERS=1
MINIO_ENDPOINT_SSL=0
# API key rate limit
API_KEY_RATE_LIMIT=60/minute
API_KEY_RATE_LIMIT="60/minute"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "live",
"version": "0.26.0",
"version": "0.25.3",
"license": "AGPL-3.0",
"description": "A realtime collaborative server powers Plane's rich text editor",
"main": "./src/server.ts",
+2 -2
View File
@@ -2,7 +2,7 @@
"name": "plane",
"description": "Open-source project management that unlocks customer value",
"repository": "https://github.com/makeplane/plane.git",
"version": "0.26.0",
"version": "0.25.3",
"license": "AGPL-3.0",
"private": true,
"workspaces": [
@@ -24,7 +24,7 @@
"devDependencies": {
"prettier": "latest",
"prettier-plugin-tailwindcss": "^0.5.4",
"turbo": "^2.5.0"
"turbo": "^2.4.2"
},
"resolutions": {
"nanoid": "3.3.8",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@plane/constants",
"version": "0.26.0",
"version": "0.25.3",
"private": true,
"main": "./src/index.ts",
"license": "AGPL-3.0"
+69 -75
View File
@@ -1,97 +1,91 @@
import { TInboxDuplicateIssueDetails, TIssue } from "@plane/types";
export enum EInboxIssueCurrentTab {
OPEN = "open",
CLOSED = "closed",
OPEN = "open",
CLOSED = "closed",
}
export enum EInboxIssueStatus {
PENDING = -2,
DECLINED = -1,
SNOOZED = 0,
ACCEPTED = 1,
DUPLICATE = 2,
}
export enum EInboxIssueSource {
IN_APP = "IN_APP",
FORMS = "FORMS",
EMAIL = "EMAIL",
PENDING = -2,
DECLINED = -1,
SNOOZED = 0,
ACCEPTED = 1,
DUPLICATE = 2,
}
export type TInboxIssueCurrentTab = EInboxIssueCurrentTab;
export type TInboxIssueStatus = EInboxIssueStatus;
export type TInboxIssue = {
id: string;
status: TInboxIssueStatus;
snoozed_till: Date | null;
duplicate_to: string | undefined;
source: EInboxIssueSource | undefined;
issue: TIssue;
created_by: string;
duplicate_issue_detail: TInboxDuplicateIssueDetails | undefined;
id: string;
status: TInboxIssueStatus;
snoozed_till: Date | null;
duplicate_to: string | undefined;
source: string;
issue: TIssue;
created_by: string;
duplicate_issue_detail: TInboxDuplicateIssueDetails | undefined;
};
export const INBOX_STATUS: {
key: string;
status: TInboxIssueStatus;
i18n_title: string;
i18n_description: () => string;
key: string;
status: TInboxIssueStatus;
i18n_title: string;
i18n_description: () => string;
}[] = [
{
key: "pending",
i18n_title: "inbox_issue.status.pending.title",
status: EInboxIssueStatus.PENDING,
i18n_description: () => `inbox_issue.status.pending.description`,
},
{
key: "declined",
i18n_title: "inbox_issue.status.declined.title",
status: EInboxIssueStatus.DECLINED,
i18n_description: () => `inbox_issue.status.declined.description`,
},
{
key: "snoozed",
i18n_title: "inbox_issue.status.snoozed.title",
status: EInboxIssueStatus.SNOOZED,
i18n_description: () => `inbox_issue.status.snoozed.description`,
},
{
key: "accepted",
i18n_title: "inbox_issue.status.accepted.title",
status: EInboxIssueStatus.ACCEPTED,
i18n_description: () => `inbox_issue.status.accepted.description`,
},
{
key: "duplicate",
i18n_title: "inbox_issue.status.duplicate.title",
status: EInboxIssueStatus.DUPLICATE,
i18n_description: () => `inbox_issue.status.duplicate.description`,
},
{
key: "pending",
i18n_title: "inbox_issue.status.pending.title",
status: EInboxIssueStatus.PENDING,
i18n_description: () => `inbox_issue.status.pending.description`,
},
{
key: "declined",
i18n_title: "inbox_issue.status.declined.title",
status: EInboxIssueStatus.DECLINED,
i18n_description: () => `inbox_issue.status.declined.description`,
},
{
key: "snoozed",
i18n_title: "inbox_issue.status.snoozed.title",
status: EInboxIssueStatus.SNOOZED,
i18n_description: () => `inbox_issue.status.snoozed.description`,
},
{
key: "accepted",
i18n_title: "inbox_issue.status.accepted.title",
status: EInboxIssueStatus.ACCEPTED,
i18n_description: () => `inbox_issue.status.accepted.description`,
},
{
key: "duplicate",
i18n_title: "inbox_issue.status.duplicate.title",
status: EInboxIssueStatus.DUPLICATE,
i18n_description: () => `inbox_issue.status.duplicate.description`,
},
];
export const INBOX_ISSUE_ORDER_BY_OPTIONS = [
{
key: "issue__created_at",
i18n_label: "inbox_issue.order_by.created_at",
},
{
key: "issue__updated_at",
i18n_label: "inbox_issue.order_by.updated_at",
},
{
key: "issue__sequence_id",
i18n_label: "inbox_issue.order_by.id",
},
{
key: "issue__created_at",
i18n_label: "inbox_issue.order_by.created_at",
},
{
key: "issue__updated_at",
i18n_label: "inbox_issue.order_by.updated_at",
},
{
key: "issue__sequence_id",
i18n_label: "inbox_issue.order_by.id",
},
];
export const INBOX_ISSUE_SORT_BY_OPTIONS = [
{
key: "asc",
i18n_label: "common.sort.asc",
},
{
key: "desc",
i18n_label: "common.sort.desc",
},
{
key: "asc",
i18n_label: "common.sort.asc",
},
{
key: "desc",
i18n_label: "common.sort.desc",
},
];
-2
View File
@@ -14,7 +14,6 @@ export * from "./state";
export * from "./swr";
export * from "./tab-indices";
export * from "./user";
export * from "./payment";
export * from "./workspace";
export * from "./stickies";
export * from "./cycle";
@@ -31,4 +30,3 @@ export * from "./spreadsheet";
export * from "./dashboard";
export * from "./page";
export * from "./emoji";
export * from "./subscription";
+69 -1
View File
@@ -1,4 +1,4 @@
import { TUnreadNotificationsCount } from "@plane/types";
import { TUnreadNotificationsCount, TNotificationSettings } from "@plane/types";
export enum ENotificationTab {
ALL = "all",
@@ -135,3 +135,71 @@ export const allTimeIn30MinutesInterval12HoursFormat: Array<{
{ label: "11:00", value: "11:00" },
{ label: "11:30", value: "11:30" },
];
export enum ENotificationSettingsKey {
PROPERTY_CHANGE = "property_change",
STATE_CHANGE = "state_change",
PRIORITY = "priority",
ASSIGNEE = "assignee",
START_DUE_DATE = "start_due_date",
COMMENTS = "comment",
MENTIONED_COMMENTS = "mention",
COMMENT_REACTIONS = "comment_reactions",
}
export enum EWorkspaceNotificationTransport {
EMAIL = "EMAIL",
IN_APP = "IN_APP",
}
export const TASK_UPDATES_NOTIFICATION_SETTINGS: TNotificationSettings[] = [
{
key: ENotificationSettingsKey.PROPERTY_CHANGE,
i18n_title: "notification_settings.work_item_property_title",
i18n_subtitle: "notification_settings.work_item_property_subtitle",
},
{
key: ENotificationSettingsKey.STATE_CHANGE,
i18n_title: "notification_settings.status_title",
i18n_subtitle: "notification_settings.status_subtitle",
},
{
key: ENotificationSettingsKey.PRIORITY,
i18n_title: "notification_settings.priority_title",
i18n_subtitle: "notification_settings.priority_subtitle",
},
{
key: ENotificationSettingsKey.ASSIGNEE,
i18n_title: "notification_settings.assignee_title",
i18n_subtitle: "notification_settings.assignee_subtitle",
},
{
key: ENotificationSettingsKey.START_DUE_DATE,
i18n_title: "notification_settings.due_date_title",
i18n_subtitle: "notification_settings.due_date_subtitle",
}
]
export const COMMENT_NOTIFICATION_SETTINGS: TNotificationSettings[] = [
{
key: ENotificationSettingsKey.MENTIONED_COMMENTS,
i18n_title: "notification_settings.mentioned_comments_title",
i18n_subtitle: "notification_settings.mentioned_comments_subtitle",
},
{
key: ENotificationSettingsKey.COMMENTS,
i18n_title: "notification_settings.new_comments_title",
i18n_subtitle: "notification_settings.new_comments_subtitle",
},
{
key: ENotificationSettingsKey.COMMENT_REACTIONS,
i18n_title: "notification_settings.reaction_comments_title",
i18n_subtitle: "notification_settings.reaction_comments_subtitle",
},
]
export const NOTIFICATION_SETTINGS: TNotificationSettings[] = [
...TASK_UPDATES_NOTIFICATION_SETTINGS,
...COMMENT_NOTIFICATION_SETTINGS
]
-163
View File
@@ -1,163 +0,0 @@
import { IPaymentProduct, TBillingFrequency, TProductBillingFrequency } from "@plane/types";
/**
* Enum representing different product subscription types
*/
export enum EProductSubscriptionEnum {
FREE = "FREE",
ONE = "ONE",
PRO = "PRO",
BUSINESS = "BUSINESS",
ENTERPRISE = "ENTERPRISE",
}
/**
* Default billing frequency for each product subscription type
*/
export const DEFAULT_PRODUCT_BILLING_FREQUENCY: TProductBillingFrequency = {
[EProductSubscriptionEnum.FREE]: undefined,
[EProductSubscriptionEnum.ONE]: undefined,
[EProductSubscriptionEnum.PRO]: "month",
[EProductSubscriptionEnum.BUSINESS]: "month",
[EProductSubscriptionEnum.ENTERPRISE]: "month",
};
/**
* Subscription types that support billing frequency toggle (monthly/yearly)
*/
export const SUBSCRIPTION_WITH_BILLING_FREQUENCY = [
EProductSubscriptionEnum.PRO,
EProductSubscriptionEnum.BUSINESS,
EProductSubscriptionEnum.ENTERPRISE,
];
/**
* Mapping of product subscription types to their respective payment product details
* Used to provide information about each product's pricing and features
*/
export const PLANE_COMMUNITY_PRODUCTS: Record<string, IPaymentProduct> = {
[EProductSubscriptionEnum.PRO]: {
id: EProductSubscriptionEnum.PRO,
name: "Plane Pro",
description:
"More views, more cycles powers, more pages features, new reports, and better dashboards are waiting to be unlocked.",
type: "PRO",
prices: [
{
id: `price_monthly_${EProductSubscriptionEnum.PRO}`,
unit_amount: 800,
recurring: "month",
currency: "usd",
workspace_amount: 800,
product: EProductSubscriptionEnum.PRO,
},
{
id: `price_yearly_${EProductSubscriptionEnum.PRO}`,
unit_amount: 7200,
recurring: "year",
currency: "usd",
workspace_amount: 7200,
product: EProductSubscriptionEnum.PRO,
},
],
payment_quantity: 1,
is_active: true,
},
[EProductSubscriptionEnum.BUSINESS]: {
id: EProductSubscriptionEnum.BUSINESS,
name: "Plane Business",
description:
"The earliest packaging of Business at $10 a seat a month billed annually, $12 a seat a month billed monthly for Plane Cloud",
type: "BUSINESS",
prices: [
{
id: `price_yearly_${EProductSubscriptionEnum.BUSINESS}`,
unit_amount: 0,
recurring: "year",
currency: "usd",
workspace_amount: 0,
product: EProductSubscriptionEnum.BUSINESS,
},
{
id: `price_monthly_${EProductSubscriptionEnum.BUSINESS}`,
unit_amount: 0,
recurring: "month",
currency: "usd",
workspace_amount: 0,
product: EProductSubscriptionEnum.BUSINESS,
},
],
payment_quantity: 1,
is_active: false,
},
[EProductSubscriptionEnum.ENTERPRISE]: {
id: EProductSubscriptionEnum.ENTERPRISE,
name: "Plane Enterprise",
description: "",
type: "ENTERPRISE",
prices: [
{
id: `price_yearly_${EProductSubscriptionEnum.ENTERPRISE}`,
unit_amount: 0,
recurring: "year",
currency: "usd",
workspace_amount: 0,
product: EProductSubscriptionEnum.ENTERPRISE,
},
{
id: `price_monthly_${EProductSubscriptionEnum.ENTERPRISE}`,
unit_amount: 0,
recurring: "month",
currency: "usd",
workspace_amount: 0,
product: EProductSubscriptionEnum.ENTERPRISE,
},
],
payment_quantity: 1,
is_active: false,
},
};
/**
* URL for the "Talk to Sales" page where users can contact sales team
*/
export const TALK_TO_SALES_URL = "https://plane.so/talk-to-sales";
/**
* Mapping of subscription types to their respective upgrade/redirection URLs based on billing frequency
* Used for self-hosted installations to redirect users to appropriate upgrade pages
*/
export const SUBSCRIPTION_REDIRECTION_URLS: Record<EProductSubscriptionEnum, Record<TBillingFrequency, string>> = {
[EProductSubscriptionEnum.FREE]: {
month: TALK_TO_SALES_URL,
year: TALK_TO_SALES_URL,
},
[EProductSubscriptionEnum.ONE]: {
month: TALK_TO_SALES_URL,
year: TALK_TO_SALES_URL,
},
[EProductSubscriptionEnum.PRO]: {
month: "https://app.plane.so/upgrade/pro/self-hosted?plan=month",
year: "https://app.plane.so/upgrade/pro/self-hosted?plan=year",
},
[EProductSubscriptionEnum.BUSINESS]: {
month: TALK_TO_SALES_URL,
year: TALK_TO_SALES_URL,
},
[EProductSubscriptionEnum.ENTERPRISE]: {
month: TALK_TO_SALES_URL,
year: TALK_TO_SALES_URL,
},
};
/**
* Mapping of subscription types to their respective marketing webpage URLs
* Used to direct users to learn more about each plan's features and pricing
*/
export const SUBSCRIPTION_WEBPAGE_URLS: Record<EProductSubscriptionEnum, string> = {
[EProductSubscriptionEnum.FREE]: TALK_TO_SALES_URL,
[EProductSubscriptionEnum.ONE]: TALK_TO_SALES_URL,
[EProductSubscriptionEnum.PRO]: "https://plane.so/pro",
[EProductSubscriptionEnum.BUSINESS]: "https://plane.so/business",
[EProductSubscriptionEnum.ENTERPRISE]: "https://plane.so/business",
};
-42
View File
@@ -1,42 +0,0 @@
export const ENTERPRISE_PLAN_FEATURES = [
"Private + managed deployments",
"GAC",
"LDAP support",
"Databases + Formulas",
"Unlimited and full Automation Flows",
"Full-suite professional services",
];
export const BUSINESS_PLAN_FEATURES = [
"Project Templates",
"Workflows + Approvals",
"Decision + Loops Automation",
"Custom Reports",
"Nested Pages",
"Intake Forms",
];
export const PRO_PLAN_FEATURES = [
"Dashboards + Reports",
"Full Time Tracking + Bulk Ops",
"Teamspaces",
"Trigger And Action",
"Wikis",
"Popular integrations",
];
export const ONE_PLAN_FEATURES = [
"OIDC + SAML for SSO",
"Active Cycles",
"Real-time collab + public views and page",
"Link pages in issues and vice-versa",
"Time-tracking + limited bulk ops",
"Docker, Kubernetes and more",
];
export const FREE_PLAN_UPGRADE_FEATURES = [
"OIDC + SAML for SSO",
"Time Tracking and Bulk Ops",
"Integrations",
"Public Views and Pages",
];
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@plane/editor",
"version": "0.26.0",
"version": "0.25.3",
"description": "Core Editor that powers Plane",
"license": "AGPL-3.0",
"private": true,
@@ -7,7 +7,7 @@ import { CustomImageNode, UploadImageExtensionStorage } from "@/extensions/custo
import { TReadOnlyFileHandler } from "@/types";
export const CustomReadOnlyImageExtension = (props: TReadOnlyFileHandler) => {
const { getAssetSrc, restore: restoreImageFn } = props;
const { getAssetSrc } = props;
return Image.extend<Record<string, unknown>, UploadImageExtensionStorage>({
name: "imageComponent",
@@ -66,9 +66,6 @@ export const CustomReadOnlyImageExtension = (props: TReadOnlyFileHandler) => {
addCommands() {
return {
getImageSource: (path: string) => async () => await getAssetSrc(path),
restoreImage: (src) => async () => {
await restoreImageFn(src);
},
};
},
+12 -12
View File
@@ -151,11 +151,11 @@
/* end font size and style */
/* layout config */
#page-toolbar-container {
container-name: page-toolbar-container;
#page-header-container {
container-name: page-header-container;
container-type: inline-size;
.page-toolbar-content {
.page-header-content {
--header-width: var(--normal-content-width);
&.wide-layout {
@@ -186,23 +186,23 @@
}
/* keep a static padding of 96px for wide layouts for container width >912px and <1344px */
@container page-toolbar-container (min-width: 912px) and (max-width: 1344px) {
.page-toolbar-content.wide-layout {
@container page-header-container (min-width: 912px) and (max-width: 1344px) {
.page-header-content.wide-layout {
padding-left: var(--wide-content-margin) !important;
}
}
/* keep a static padding of 96px for wide layouts for container width <912px */
@container page-toolbar-container (max-width: 912) {
.page-toolbar-content.wide-layout {
@container page-header-container (max-width: 912) {
.page-header-content.wide-layout {
padding-left: var(--wide-content-margin) !important;
}
}
/* end layout config */
/* keep a static padding of 20px for wide layouts for container width <760px */
@container page-toolbar-container (max-width: 760px) {
.page-toolbar-content {
@container page-header-container (max-width: 760px) {
.page-header-content {
padding-left: var(--normal-content-margin) !important;
}
}
@@ -211,7 +211,7 @@
/* keep a static padding of 96px for wide layouts for container width >912px and <1344px */
@container page-content-container (min-width: 912px) and (max-width: 1344px) {
.editor-container.wide-layout,
.page-header-container {
.page-title-container {
padding-left: var(--wide-content-margin);
padding-right: var(--wide-content-margin);
}
@@ -220,7 +220,7 @@
/* keep a static padding of 20px for wide layouts for container width <912px */
@container page-content-container (max-width: 912px) {
.editor-container.wide-layout,
.page-header-container {
.page-title-container {
padding-left: var(--normal-content-margin);
padding-right: var(--normal-content-margin);
}
@@ -229,7 +229,7 @@
/* keep a static padding of 20px for normal layouts for container width <760px */
@container page-content-container (max-width: 760px) {
.editor-container:not(.wide-layout),
.page-header-container {
.page-title-container {
padding-left: var(--normal-content-margin);
padding-right: var(--normal-content-margin);
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@plane/eslint-config",
"private": true,
"version": "0.26.0",
"version": "0.25.3",
"license": "AGPL-3.0",
"files": [
"library.js",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@plane/hooks",
"version": "0.26.0",
"version": "0.25.3",
"license": "AGPL-3.0",
"description": "React hooks that are shared across multiple apps internally",
"private": true,
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@plane/i18n",
"version": "0.26.0",
"version": "0.25.3",
"license": "AGPL-3.0",
"description": "I18n shared across multiple apps internally",
"private": true,
-1
View File
@@ -21,7 +21,6 @@ export const SUPPORTED_LANGUAGES: ILanguageOption[] = [
{ label: "Indonesian", value: "id" },
{ label: "Română", value: "ro" },
{ label: "Tiếng việt", value: "vi-VN" },
{ label: "Türkçe", value: "tr-TR" },
];
export const LANGUAGE_STORAGE_KEY = "userLanguage";
+37 -26
View File
@@ -348,7 +348,7 @@
"couldnt_remove_the_project_from_favorites": "Nepodařilo se odstranit projekt z oblíbených. Zkuste to prosím znovu.",
"add_to_favorites": "Přidat do oblíbených",
"remove_from_favorites": "Odebrat z oblíbených",
"publish_project": "Publikovat projekt",
"publish_settings": "Nastavení publikování",
"publish": "Publikovat",
"copy_link": "Kopírovat odkaz",
"leave_project": "Opustit projekt",
@@ -867,8 +867,7 @@
"deleting": "Mazání",
"pending": "Čekající",
"invite": "Pozvat",
"view": "Pohled",
"deactivated_user": "Deaktivovaný uživatel"
"view": "Pohled"
},
"chart": {
@@ -1739,15 +1738,12 @@
"title": "Povolit odhady pro můj projekt",
"description": "Pomáhají vám komunikovat složitost a pracovní zátěž týmu.",
"no_estimate": "Bez odhadu",
"new": "Nový systém odhadů",
"create": {
"custom": "Vlastní",
"start_from_scratch": "Začít od nuly",
"choose_template": "Vybrat šablonu",
"choose_estimate_system": "Vybrat systém odhadů",
"enter_estimate_point": "Zadat odhad",
"step": "Krok {step} z {total}",
"label": "Vytvořit odhad"
"enter_estimate_point": "Zadat odhad"
},
"toasts": {
"created": {
@@ -1796,25 +1792,6 @@
"already_exists": "Hodnota odhadu již existuje.",
"unsaved_changes": "Máte neuložené změny. Před kliknutím na hotovo je prosím uložte",
"remove_empty": "Odhad nemůže být prázdný. Zadejte hodnotu do každého pole nebo odstraňte ta, pro která nemáte hodnoty."
},
"systems": {
"points": {
"label": "Body",
"fibonacci": "Fibonacci",
"linear": "Lineární",
"squares": "Čtverce",
"custom": "Vlastní"
},
"categories": {
"label": "Kategorie",
"t_shirt_sizes": "Velikosti triček",
"easy_to_hard": "Od snadného po těžké",
"custom": "Vlastní"
},
"time": {
"label": "Čas",
"hours": "Hodiny"
}
}
},
"automations": {
@@ -2458,5 +2435,39 @@
"last_edited_by": "Naposledy upraveno uživatelem",
"previously_edited_by": "Dříve upraveno uživatelem",
"edited_by": "Upraveno uživatelem"
},
"notification_settings": {
"page_label": "{workspace} - Nastavení doručené pošty",
"inbox_settings": "Nastavení doručené pošty",
"inbox_settings_description": "Přizpůsobte si, jak dostáváte oznámení o aktivitách ve vašem pracovním prostoru. Vaše změny jsou automaticky uloženy.",
"advanced_settings": "Pokročilá nastavení",
"in_plane": "V Plane",
"email": "Email",
"slack": "Slack",
"task_updates": "Aktualizace pracovních položek",
"task_updates_subtitle": "Dostávejte oznámení, když jsou aktualizovány pracovní položky ve vašem pracovním prostoru.",
"comments": "Komentáře",
"comments_subtitle": "Buďte informováni o diskusích ve vašem pracovním prostoru.",
"work_item_property_title": "Aktualizace jakékoli vlastnosti pracovního položky",
"work_item_property_subtitle": "Dostávejte oznámení, když jsou aktualizovány pracovní položky ve vašem pracovním prostoru.",
"status_title": "Změna stavu",
"status_subtitle": "Když je aktualizován stav pracovního položky.",
"priority_title": "Změna priority",
"priority_subtitle": "Když je upravena priorita pracovního položky.",
"assignee_title": "Změna přiřazení",
"assignee_subtitle": "Když je pracovní položka přiřazena nebo přeřazena někomu.",
"due_date_title": "Změna data",
"due_date_subtitle": "Když je aktualizováno datum zahájení nebo splatnosti pracovního položky.",
"module_title": "Aktualizace modulu",
"cycle_title": "Aktualizace cyklu",
"mentioned_comments_title": "Zmínky",
"mentioned_comments_subtitle": "Když vás někdo zmíní v komentáři.",
"new_comments_title": "Nové komentáře",
"new_comments_subtitle": "Když je přidán nový komentář k úkolu, který sledujete.",
"reaction_comments_title": "Reakce",
"reaction_comments_subtitle": "Dostávejte oznámení, když někdo reaguje na vaše komentáře nebo úkoly pomocí emoji.",
"setting_updated_successfully": "Nastavení bylo úspěšně aktualizováno",
"failed_to_update_setting": "Nepodařilo se aktualizovat nastavení"
}
}
+37 -26
View File
@@ -348,7 +348,7 @@
"couldnt_remove_the_project_from_favorites": "Projekt konnte nicht aus den Favoriten entfernt werden. Bitte versuchen Sie es erneut.",
"add_to_favorites": "Zu Favoriten hinzufügen",
"remove_from_favorites": "Aus Favoriten entfernen",
"publish_project": "Projekt veröffentlichen",
"publish_settings": "Veröffentlichungseinstellungen",
"publish": "Veröffentlichen",
"copy_link": "Link kopieren",
"leave_project": "Projekt verlassen",
@@ -862,8 +862,7 @@
"deleting": "Wird gelöscht",
"pending": "Ausstehend",
"invite": "Einladen",
"view": "Ansicht",
"deactivated_user": "Deaktivierter Benutzer"
"view": "Ansicht"
},
"chart": {
"x_axis": "X-Achse",
@@ -1715,15 +1714,12 @@
"title": "Schätzungen für mein Projekt aktivieren",
"description": "Sie helfen dir, die Komplexität und Arbeitsbelastung des Teams zu kommunizieren.",
"no_estimate": "Keine Schätzung",
"new": "Neues Schätzungssystem",
"create": {
"custom": "Benutzerdefiniert",
"start_from_scratch": "Von Grund auf neu",
"choose_template": "Vorlage wählen",
"choose_estimate_system": "Schätzungssystem wählen",
"enter_estimate_point": "Schätzung eingeben",
"step": "Schritt {step} von {total}",
"label": "Schätzung erstellen"
"enter_estimate_point": "Schätzung eingeben"
},
"toasts": {
"created": {
@@ -1772,25 +1768,6 @@
"already_exists": "Der Schätzungswert existiert bereits.",
"unsaved_changes": "Du hast ungespeicherte Änderungen. Bitte speichere sie, bevor du auf Fertig klickst",
"remove_empty": "Die Schätzung darf nicht leer sein. Gib einen Wert in jedes Feld ein oder entferne die Felder, für die du keine Werte hast."
},
"systems": {
"points": {
"label": "Punkte",
"fibonacci": "Fibonacci",
"linear": "Linear",
"squares": "Quadrate",
"custom": "Benutzerdefiniert"
},
"categories": {
"label": "Kategorien",
"t_shirt_sizes": "T-Shirt-Größen",
"easy_to_hard": "Einfach bis schwer",
"custom": "Benutzerdefiniert"
},
"time": {
"label": "Zeit",
"hours": "Stunden"
}
}
},
"automations": {
@@ -2416,5 +2393,39 @@
"last_edited_by": "Zuletzt bearbeitet von",
"previously_edited_by": "Zuvor bearbeitet von",
"edited_by": "Bearbeitet von"
},
"notification_settings": {
"page_label": "{workspace} - Posteingangseinstellungen",
"inbox_settings": "Posteingangseinstellungen",
"inbox_settings_description": "Passen Sie an, wie Sie Benachrichtigungen für Aktivitäten in Ihrem Arbeitsbereich erhalten. Ihre Änderungen werden automatisch gespeichert.",
"advanced_settings": "Erweiterte Einstellungen",
"in_plane": "In Plane",
"email": "E-Mail",
"slack": "Slack",
"task_updates": "Aktualisierungen von Arbeitselementen",
"task_updates_subtitle": "Erhalten Sie Benachrichtigungen, wenn Arbeitselemente in Ihrem Arbeitsbereich aktualisiert werden.",
"comments": "Kommentare",
"comments_subtitle": "Bleiben Sie über Diskussionen in Ihrem Arbeitsbereich auf dem Laufenden.",
"work_item_property_title": "Aktualisierung einer Eigenschaft des Arbeitselements",
"work_item_property_subtitle": "Erhalten Sie Benachrichtigungen, wenn Arbeitselemente in Ihrem Arbeitsbereich aktualisiert werden.",
"status_title": "Statusänderung",
"status_subtitle": "Wenn der Status eines Arbeitselements aktualisiert wird.",
"priority_title": "Prioritätsänderung",
"priority_subtitle": "Wenn das Prioritätsniveau eines Arbeitselements angepasst wird.",
"assignee_title": "Zuweisungsänderung",
"assignee_subtitle": "Wenn ein Arbeitselement jemandem zugewiesen oder neu zugewiesen wird.",
"due_date_title": "Datumsänderung",
"due_date_subtitle": "Wenn das Start- oder Fälligkeitsdatum eines Arbeitselements aktualisiert wird.",
"module_title": "Modulaktualisierung",
"cycle_title": "Zyklusaktualisierung",
"mentioned_comments_title": "Erwähnungen",
"mentioned_comments_subtitle": "Wenn jemand Sie in einem Kommentar erwähnt.",
"new_comments_title": "Neue Kommentare",
"new_comments_subtitle": "Wenn ein neuer Kommentar zu einer Aufgabe hinzugefügt wird, die Sie verfolgen.",
"reaction_comments_title": "Reaktionen",
"reaction_comments_subtitle": "Erhalten Sie Benachrichtigungen, wenn jemand mit einem Emoji auf Ihre Kommentare oder Aufgaben reagiert.",
"setting_updated_successfully": "Einstellung erfolgreich aktualisiert",
"failed_to_update_setting": "Fehler beim Aktualisieren der Einstellung"
}
}
+37 -26
View File
@@ -180,7 +180,7 @@
"couldnt_remove_the_project_from_favorites": "Couldn't remove the project from favorites. Please try again.",
"add_to_favorites": "Add to favorites",
"remove_from_favorites": "Remove from favorites",
"publish_project": "Publish project",
"publish_settings": "Publish settings",
"publish": "Publish",
"copy_link": "Copy link",
"leave_project": "Leave project",
@@ -702,8 +702,7 @@
"deleting": "Deleting",
"pending": "Pending",
"invite": "Invite",
"view": "View",
"deactivated_user": "Deactivated user"
"view": "View"
},
"chart": {
@@ -1574,15 +1573,12 @@
"title": "Enable estimates for my project",
"description": "They help you in communicating complexity and workload of the team.",
"no_estimate": "No estimate",
"new": "New estimate system",
"create": {
"custom": "Custom",
"start_from_scratch": "Start from scratch",
"choose_template": "Choose a template",
"choose_estimate_system": "Choose an estimate system",
"enter_estimate_point": "Enter estimate",
"step": "Step {step} of {total}",
"label": "Create estimate"
"enter_estimate_point": "Enter estimate"
},
"toasts": {
"created": {
@@ -1631,25 +1627,6 @@
"already_exists": "Estimate value already exists.",
"unsaved_changes": "You have some unsaved changes, Please save them before clicking on done",
"remove_empty": "Estimate can't be empty. Enter a value in each field or remove those you don't have values for."
},
"systems": {
"points": {
"label": "Points",
"fibonacci": "Fibonacci",
"linear": "Linear",
"squares": "Squares",
"custom": "Custom"
},
"categories": {
"label": "Categories",
"t_shirt_sizes": "T-Shirt Sizes",
"easy_to_hard": "Easy to hard",
"custom": "Custom"
},
"time": {
"label": "Time",
"hours": "Hours"
}
}
},
"automations": {
@@ -2293,5 +2270,39 @@
"last_edited_by": "Last edited by",
"previously_edited_by": "Previously edited by",
"edited_by": "Edited by"
},
"notification_settings": {
"page_label": "{workspace} - Inbox settings",
"inbox_settings": "Inbox settings",
"inbox_settings_description": "Customize how you receive notifications for activities in your workspace. Your changes are saved automatically.",
"advanced_settings": "Advanced settings",
"in_plane": "In Plane",
"email": "Email",
"slack": "Slack",
"task_updates": "Work item updates",
"task_updates_subtitle": "Get notified when work items in your workspace are updated.",
"comments": "Comments",
"comments_subtitle": "Stay updated on discussions in your workspace.",
"work_item_property_title": "Update on any property of the work item",
"work_item_property_subtitle": "Get notified when work items in your workspace are updated.",
"status_title": "State change",
"status_subtitle": "When a work item's state is updated.",
"priority_title": "Priority change",
"priority_subtitle": "When a work item's priority level is adjusted.",
"assignee_title": "Assignee change",
"assignee_subtitle": "When a work item is assigned or reassigned to someone.",
"due_date_title": "Date change",
"due_date_subtitle": "When a work item's start or due date is updated.",
"module_title": "Module update",
"cycle_title": "Cycle update",
"mentioned_comments_title": "Mentions",
"mentioned_comments_subtitle": "When someone mentions you in a comment.",
"new_comments_title": "New comments",
"new_comments_subtitle": "When a new comment is added to a task youre following.",
"reaction_comments_title": "Reactions",
"reaction_comments_subtitle": "Get notified when someone reacts to your comments or tasks with an emoji.",
"setting_updated_successfully": "Setting updated successfully",
"failed_to_update_setting": "Failed to update setting"
}
}
+37 -26
View File
@@ -352,7 +352,7 @@
"couldnt_remove_the_project_from_favorites": "No se pudo eliminar el proyecto de favoritos. Por favor, inténtalo de nuevo.",
"add_to_favorites": "Agregar a favoritos",
"remove_from_favorites": "Eliminar de favoritos",
"publish_project": "Publicar proyecto",
"publish_settings": "Configuración de publicación",
"publish": "Publicar",
"copy_link": "Copiar enlace",
"leave_project": "Abandonar proyecto",
@@ -872,8 +872,7 @@
"deleting": "Eliminando",
"pending": "Pendiente",
"invite": "Invitar",
"view": "Ver",
"deactivated_user": "Usuario desactivado"
"view": "Ver"
},
"chart": {
@@ -1743,15 +1742,12 @@
"title": "Activar estimaciones para mi proyecto",
"description": "Te ayudan a comunicar la complejidad y la carga de trabajo del equipo.",
"no_estimate": "Sin estimación",
"new": "Nuevo sistema de estimación",
"create": {
"custom": "Personalizado",
"start_from_scratch": "Comenzar desde cero",
"choose_template": "Elegir una plantilla",
"choose_estimate_system": "Elegir un sistema de estimación",
"enter_estimate_point": "Ingresar estimación",
"step": "Paso {step} de {total}",
"label": "Crear estimación"
"enter_estimate_point": "Ingresar estimación"
},
"toasts": {
"created": {
@@ -1800,25 +1796,6 @@
"already_exists": "El valor de la estimación ya existe.",
"unsaved_changes": "Tienes cambios sin guardar. Por favor guárdalos antes de hacer clic en Hecho",
"remove_empty": "La estimación no puede estar vacía. Ingresa un valor en cada campo o elimina aquellos para los que no tienes valores."
},
"systems": {
"points": {
"label": "Puntos",
"fibonacci": "Fibonacci",
"linear": "Lineal",
"squares": "Cuadrados",
"custom": "Personalizado"
},
"categories": {
"label": "Categorías",
"t_shirt_sizes": "Tallas de camiseta",
"easy_to_hard": "Fácil a difícil",
"custom": "Personalizado"
},
"time": {
"label": "Tiempo",
"hours": "Horas"
}
}
},
"automations": {
@@ -2462,5 +2439,39 @@
"last_edited_by": "Última edición por",
"previously_edited_by": "Editado anteriormente por",
"edited_by": "Editado por"
},
"notification_settings": {
"page_label": "{workspace} - Configuración de la bandeja de entrada",
"inbox_settings": "Configuración de la bandeja de entrada",
"inbox_settings_description": "Personaliza cómo recibes notificaciones de actividades en tu espacio de trabajo. Tus cambios se guardan automáticamente.",
"advanced_settings": "Configuración avanzada",
"in_plane": "En Plane",
"email": "Correo electrónico",
"slack": "Slack",
"task_updates": "Actualizaciones de elementos de trabajo",
"task_updates_subtitle": "Recibe notificaciones cuando se actualicen los elementos de trabajo en tu espacio de trabajo.",
"comments": "Comentarios",
"comments_subtitle": "Mantente actualizado sobre las discusiones en tu espacio de trabajo.",
"work_item_property_title": "Actualización de cualquier propiedad del elemento de trabajo",
"work_item_property_subtitle": "Recibe notificaciones cuando se actualicen los elementos de trabajo en tu espacio de trabajo.",
"status_title": "Cambio de estado",
"status_subtitle": "Cuando se actualiza el estado de un elemento de trabajo.",
"priority_title": "Cambio de prioridad",
"priority_subtitle": "Cuando se ajusta el nivel de prioridad de un elemento de trabajo.",
"assignee_title": "Cambio de asignado",
"assignee_subtitle": "Cuando un elemento de trabajo se asigna o reasigna a alguien.",
"due_date_title": "Cambio de fecha",
"due_date_subtitle": "Cuando se actualiza la fecha de inicio o vencimiento de un elemento de trabajo.",
"module_title": "Actualización del módulo",
"cycle_title": "Actualización del ciclo",
"mentioned_comments_title": "Menciones",
"mentioned_comments_subtitle": "Cuando alguien te menciona en un comentario.",
"new_comments_title": "Nuevos comentarios",
"new_comments_subtitle": "Cuando se agrega un nuevo comentario a una tarea que sigues.",
"reaction_comments_title": "Reacciones",
"reaction_comments_subtitle": "Recibe notificaciones cuando alguien reacciona a tus comentarios o tareas con un emoji.",
"setting_updated_successfully": "Configuración actualizada con éxito",
"failed_to_update_setting": "Error al actualizar la configuración"
}
}
+38 -26
View File
@@ -350,7 +350,7 @@
"couldnt_remove_the_project_from_favorites": "Impossible de supprimer le projet des favoris. Veuillez réessayer.",
"add_to_favorites": "Ajouter aux favoris",
"remove_from_favorites": "Supprimer des favoris",
"publish_project": "Publier le projet",
"publish_settings": "Paramètres de publication",
"publish": "Publier",
"copy_link": "Copier le lien",
"leave_project": "Quitter le projet",
@@ -870,8 +870,7 @@
"deleting": "Suppression",
"pending": "En attente",
"invite": "Inviter",
"view": "Afficher",
"deactivated_user": "Utilisateur désactivé"
"view": "Afficher"
},
"chart": {
@@ -1741,15 +1740,12 @@
"title": "Activer les estimations pour mon projet",
"description": "Elles vous aident à communiquer la complexité et la charge de travail de l'équipe.",
"no_estimate": "Sans estimation",
"new": "Nouveau système d'estimation",
"create": {
"custom": "Personnalisé",
"start_from_scratch": "Commencer depuis zéro",
"choose_template": "Choisir un modèle",
"choose_estimate_system": "Choisir un système d'estimation",
"enter_estimate_point": "Saisir une estimation",
"step": "Étape {step} de {total}",
"label": "Créer une estimation"
"enter_estimate_point": "Saisir une estimation"
},
"toasts": {
"created": {
@@ -1798,25 +1794,6 @@
"already_exists": "La valeur de l'estimation existe déjà.",
"unsaved_changes": "Vous avez des modifications non enregistrées. Veuillez les enregistrer avant de cliquer sur Terminé",
"remove_empty": "L'estimation ne peut pas être vide. Saisissez une valeur dans chaque champ ou supprimez ceux pour lesquels vous n'avez pas de valeurs."
},
"systems": {
"points": {
"label": "Points",
"fibonacci": "Fibonacci",
"linear": "Linéaire",
"squares": "Carrés",
"custom": "Personnalisé"
},
"categories": {
"label": "Catégories",
"t_shirt_sizes": "Tailles de T-Shirt",
"easy_to_hard": "Facile à difficile",
"custom": "Personnalisé"
},
"time": {
"label": "Temps",
"hours": "Heures"
}
}
},
"automations": {
@@ -2460,5 +2437,40 @@
"last_edited_by": "Dernière modification par",
"previously_edited_by": "Précédemment modifié par",
"edited_by": "Modifié par"
},
"notification_settings": {
"page_label": "{workspace} - Paramètres de la boîte de réception",
"inbox_settings": "Paramètres de la boîte de réception",
"inbox_settings_description": "Personnalisez la façon dont vous recevez les notifications pour les activités dans votre espace de travail. Vos modifications sont enregistrées automatiquement.",
"advanced_settings": "Paramètres avancés",
"in_plane": "Dans Plane",
"email": "Email",
"slack": "Slack",
"task_updates": "Mises à jour des éléments de travail",
"task_updates_subtitle": "Recevez une notification lorsque les éléments de travail dans votre espace de travail sont mis à jour.",
"comments": "Commentaires",
"comments_subtitle": "Restez informé des discussions dans votre espace de travail.",
"work_item_property_title": "Mise à jour de toute propriété de l'élément de travail",
"work_item_property_subtitle": "Recevez une notification lorsque les éléments de travail dans votre espace de travail sont mis à jour.",
"status_title": "Changement d'état",
"status_subtitle": "Lorsqu'un élément de travail change d'état.",
"priority_title": "Changement de priorité",
"priority_subtitle": "Lorsqu'un niveau de priorité d'un élément de travail est ajusté.",
"assignee_title": "Changement de responsable",
"assignee_subtitle": "Lorsqu'un élément de travail est assigné ou réassigné à quelqu'un.",
"due_date_title": "Changement de date",
"due_date_subtitle": "Lorsqu'une date de début ou d'échéance d'un élément de travail est mise à jour.",
"module_title": "Mise à jour du module",
"cycle_title": "Mise à jour du cycle",
"mentioned_comments_title": "Mentions",
"mentioned_comments_subtitle": "Lorsqu'une personne vous mentionne dans un commentaire.",
"new_comments_title": "Nouveaux commentaires",
"new_comments_subtitle": "Lorsqu'un nouveau commentaire est ajouté à une tâche que vous suivez.",
"reaction_comments_title": "Réactions",
"reaction_comments_subtitle": "Recevez une notification lorsque quelqu'un réagit à vos commentaires ou tâches avec un emoji.",
"setting_updated_successfully": "Paramètre mis à jour avec succès",
"failed_to_update_setting": "Échec de la mise à jour du paramètre"
}
}
+37 -26
View File
@@ -350,7 +350,7 @@
"couldnt_remove_the_project_from_favorites": "Tidak dapat menghapus proyek dari favorit. Silakan coba lagi.",
"add_to_favorites": "Tambah ke favorit",
"remove_from_favorites": "Hapus dari favorit",
"publish_project": "Publikasikan proyek",
"publish_settings": "Pengaturan publikasi",
"publish": "Publikasikan",
"copy_link": "Salin tautan",
"leave_project": "Tinggalkan proyek",
@@ -869,8 +869,7 @@
"deleting": "Menghapus",
"pending": "Tertunda",
"invite": "Undang",
"view": "Lihat",
"deactivated_user": "Pengguna dinonaktifkan"
"view": "Lihat"
},
"chart": {
@@ -1741,15 +1740,12 @@
"title": "Aktifkan perkiraan untuk proyek saya",
"description": "Ini membantu Anda dalam mengkomunikasikan kompleksitas dan beban kerja tim.",
"no_estimate": "Tidak ada perkiraan",
"new": "Sistem perkiraan baru",
"create": {
"custom": "Kustom",
"start_from_scratch": "Mulai dari awal",
"choose_template": "Pilih template",
"choose_estimate_system": "Pilih sistem perkiraan",
"enter_estimate_point": "Masukkan perkiraan",
"step": "Langkah {step} dari {total}",
"label": "Buat perkiraan"
"enter_estimate_point": "Masukkan perkiraan"
},
"toasts": {
"created": {
@@ -1798,25 +1794,6 @@
"already_exists": "Nilai perkiraan sudah ada.",
"unsaved_changes": "Anda memiliki beberapa perubahan yang belum disimpan, Harap simpan sebelum mengklik selesai",
"remove_empty": "Perkiraan tidak boleh kosong. Masukkan nilai di setiap bidang atau hapus yang tidak memiliki nilai."
},
"systems": {
"points": {
"label": "Poin",
"fibonacci": "Fibonacci",
"linear": "Linear",
"squares": "Kuadrat",
"custom": "Kustom"
},
"categories": {
"label": "Kategori",
"t_shirt_sizes": "Ukuran Baju",
"easy_to_hard": "Mudah ke sulit",
"custom": "Kustom"
},
"time": {
"label": "Waktu",
"hours": "Jam"
}
}
},
"automations": {
@@ -2454,5 +2431,39 @@
"last_edited_by": "Terakhir disunting oleh",
"previously_edited_by": "Sebelumnya disunting oleh",
"edited_by": "Disunting oleh"
},
"notification_settings": {
"page_label": "{workspace} - Pengaturan kotak masuk",
"inbox_settings": "Pengaturan kotak masuk",
"inbox_settings_description": "Sesuaikan cara Anda menerima notifikasi untuk aktivitas di ruang kerja Anda. Perubahan Anda disimpan secara otomatis.",
"advanced_settings": "Pengaturan lanjutan",
"in_plane": "Di Plane",
"email": "Email",
"slack": "Slack",
"task_updates": "Pembaruan item kerja",
"task_updates_subtitle": "Dapatkan notifikasi ketika item kerja di ruang kerja Anda diperbarui.",
"comments": "Komentar",
"comments_subtitle": "Tetap terinformasi tentang diskusi di ruang kerja Anda.",
"work_item_property_title": "Pembaruan properti item kerja",
"work_item_property_subtitle": "Dapatkan notifikasi ketika item kerja di ruang kerja Anda diperbarui.",
"status_title": "Perubahan status",
"status_subtitle": "Ketika status item kerja diperbarui.",
"priority_title": "Perubahan prioritas",
"priority_subtitle": "Ketika tingkat prioritas item kerja disesuaikan.",
"assignee_title": "Perubahan penugasan",
"assignee_subtitle": "Ketika item kerja ditugaskan atau ditugaskan ulang kepada seseorang.",
"due_date_title": "Perubahan tanggal",
"due_date_subtitle": "Ketika tanggal mulai atau jatuh tempo item kerja diperbarui.",
"module_title": "Pembaruan modul",
"cycle_title": "Pembaruan siklus",
"mentioned_comments_title": "Penyebutan",
"mentioned_comments_subtitle": "Ketika seseorang menyebut Anda dalam komentar.",
"new_comments_title": "Komentar baru",
"new_comments_subtitle": "Ketika komentar baru ditambahkan ke tugas yang Anda ikuti.",
"reaction_comments_title": "Reaksi",
"reaction_comments_subtitle": "Dapatkan notifikasi ketika seseorang bereaksi terhadap komentar atau tugas Anda dengan emoji.",
"setting_updated_successfully": "Pengaturan berhasil diperbarui",
"failed_to_update_setting": "Gagal memperbarui pengaturan"
}
}
+37 -26
View File
@@ -349,7 +349,7 @@
"couldnt_remove_the_project_from_favorites": "Impossibile rimuovere il progetto dai preferiti. Per favore, riprova.",
"add_to_favorites": "Aggiungi ai preferiti",
"remove_from_favorites": "Rimuovi dai preferiti",
"publish_project": "Pubblica progetto",
"publish_settings": "Impostazioni di pubblicazione",
"publish": "Pubblica",
"copy_link": "Copia link",
"leave_project": "Lascia progetto",
@@ -868,8 +868,7 @@
"deleting": "Eliminazione in corso",
"pending": "In sospeso",
"invite": "Invita",
"view": "Visualizza",
"deactivated_user": "Utente disattivato"
"view": "Visualizza"
},
"chart": {
@@ -1740,15 +1739,12 @@
"title": "Abilita le stime per il mio progetto",
"description": "Ti aiutano a comunicare la complessità e il carico di lavoro del team.",
"no_estimate": "Nessuna stima",
"new": "Nuovo sistema di stima",
"create": {
"custom": "Personalizzato",
"start_from_scratch": "Inizia da zero",
"choose_template": "Scegli un modello",
"choose_estimate_system": "Scegli un sistema di stima",
"enter_estimate_point": "Inserisci stima",
"step": "Passo {step} di {total}",
"label": "Crea stima"
"enter_estimate_point": "Inserisci stima"
},
"toasts": {
"created": {
@@ -1797,25 +1793,6 @@
"already_exists": "Il valore della stima esiste già.",
"unsaved_changes": "Hai delle modifiche non salvate. Salva prima di cliccare su Fatto",
"remove_empty": "La stima non può essere vuota. Inserisci un valore in ogni campo o rimuovi quelli per cui non hai valori."
},
"systems": {
"points": {
"label": "Punti",
"fibonacci": "Fibonacci",
"linear": "Lineare",
"squares": "Quadrati",
"custom": "Personalizzato"
},
"categories": {
"label": "Categorie",
"t_shirt_sizes": "Taglie T-Shirt",
"easy_to_hard": "Da facile a difficile",
"custom": "Personalizzato"
},
"time": {
"label": "Tempo",
"hours": "Ore"
}
}
},
"automations": {
@@ -2459,5 +2436,39 @@
"last_edited_by": "Ultima modifica di",
"previously_edited_by": "Precedentemente modificato da",
"edited_by": "Modificato da"
},
"notification_settings": {
"page_label": "{workspace} - Impostazioni della casella di posta",
"inbox_settings": "Impostazioni della casella di posta",
"inbox_settings_description": "Personalizza come ricevi le notifiche per le attività nel tuo spazio di lavoro. Le tue modifiche vengono salvate automaticamente.",
"advanced_settings": "Impostazioni avanzate",
"in_plane": "In Plane",
"email": "Email",
"slack": "Slack",
"task_updates": "Aggiornamenti degli elementi di lavoro",
"task_updates_subtitle": "Ricevi una notifica quando gli elementi di lavoro nel tuo spazio di lavoro vengono aggiornati.",
"comments": "Commenti",
"comments_subtitle": "Rimani aggiornato sulle discussioni nel tuo spazio di lavoro.",
"work_item_property_title": "Aggiornamento di qualsiasi proprietà dell'elemento di lavoro",
"work_item_property_subtitle": "Ricevi una notifica quando gli elementi di lavoro nel tuo spazio di lavoro vengono aggiornati.",
"status_title": "Cambio di stato",
"status_subtitle": "Quando lo stato di un elemento di lavoro viene aggiornato.",
"priority_title": "Cambio di priorità",
"priority_subtitle": "Quando il livello di priorità di un elemento di lavoro viene regolato.",
"assignee_title": "Cambio di assegnatario",
"assignee_subtitle": "Quando un elemento di lavoro viene assegnato o riassegnato a qualcuno.",
"due_date_title": "Cambio di data",
"due_date_subtitle": "Quando la data di inizio o di scadenza di un elemento di lavoro viene aggiornata.",
"module_title": "Aggiornamento del modulo",
"cycle_title": "Aggiornamento del ciclo",
"mentioned_comments_title": "Menzioni",
"mentioned_comments_subtitle": "Quando qualcuno ti menziona in un commento.",
"new_comments_title": "Nuovi commenti",
"new_comments_subtitle": "Quando viene aggiunto un nuovo commento a un'attività che stai seguendo.",
"reaction_comments_title": "Reazioni",
"reaction_comments_subtitle": "Ricevi una notifica quando qualcuno reagisce ai tuoi commenti o attività con un'emoji.",
"setting_updated_successfully": "Impostazione aggiornata con successo",
"failed_to_update_setting": "Aggiornamento dell'impostazione non riuscito"
}
}
+37 -26
View File
@@ -350,7 +350,7 @@
"couldnt_remove_the_project_from_favorites": "プロジェクトをお気に入りから削除できませんでした。もう一度お試しください。",
"add_to_favorites": "お気に入りに追加",
"remove_from_favorites": "お気に入りから削除",
"publish_project": "プロジェクトを公開",
"publish_settings": "公開設定",
"publish": "公開",
"copy_link": "リンクをコピー",
"leave_project": "プロジェクトを退出",
@@ -870,8 +870,7 @@
"deleting": "デリーティング",
"pending": "保留中",
"invite": "招待",
"view": "ビュー",
"deactivated_user": "無効化されたユーザー"
"view": "ビュー"
},
"chart": {
@@ -1741,15 +1740,12 @@
"title": "プロジェクトの見積もりを有効にする",
"description": "チームの複雑さと作業負荷を伝えるのに役立ちます。",
"no_estimate": "見積もりなし",
"new": "新しい見積もりシステム",
"create": {
"custom": "カスタム",
"start_from_scratch": "最初から開始",
"choose_template": "テンプレートを選択",
"choose_estimate_system": "見積もりシステムを選択",
"enter_estimate_point": "見積もりを入力",
"step": "ステップ {step} の {total}",
"label": "見積もりを作成"
"enter_estimate_point": "見積もりを入力"
},
"toasts": {
"created": {
@@ -1798,25 +1794,6 @@
"already_exists": "見積もり値は既に存在します。",
"unsaved_changes": "未保存の変更があります。完了をクリックする前に保存してください",
"remove_empty": "見積もりは空にできません。各フィールドに値を入力するか、値がないフィールドを削除してください。"
},
"systems": {
"points": {
"label": "ポイント",
"fibonacci": "フィボナッチ",
"linear": "リニア",
"squares": "二乗",
"custom": "カスタム"
},
"categories": {
"label": "カテゴリー",
"t_shirt_sizes": "Tシャツサイズ",
"easy_to_hard": "簡単から難しい",
"custom": "カスタム"
},
"time": {
"label": "時間",
"hours": "時間"
}
}
},
"automations": {
@@ -2460,5 +2437,39 @@
"last_edited_by": "最終編集者",
"previously_edited_by": "以前の編集者",
"edited_by": "編集者"
},
"notification_settings": {
"page_label": "{workspace} - 受信トレイ設定",
"inbox_settings": "受信トレイ設定",
"inbox_settings_description": "ワークスペース内のアクティビティに関する通知の受け取り方法をカスタマイズします。変更は自動的に保存されます。",
"advanced_settings": "詳細設定",
"in_plane": "Plane内",
"email": "メール",
"slack": "Slack",
"task_updates": "作業項目の更新",
"task_updates_subtitle": "ワークスペース内の作業項目が更新されたときに通知を受け取ります。",
"comments": "コメント",
"comments_subtitle": "ワークスペース内のディスカッションを最新の状態に保ちます。",
"work_item_property_title": "作業項目のプロパティの更新",
"work_item_property_subtitle": "ワークスペース内の作業項目が更新されたときに通知を受け取ります。",
"status_title": "状態の変更",
"status_subtitle": "作業項目の状態が更新されたとき。",
"priority_title": "優先度の変更",
"priority_subtitle": "作業項目の優先度レベルが調整されたとき。",
"assignee_title": "担当者の変更",
"assignee_subtitle": "作業項目が誰かに割り当てられたり再割り当てされたとき。",
"due_date_title": "日付の変更",
"due_date_subtitle": "作業項目の開始日または期限が更新されたとき。",
"module_title": "モジュールの更新",
"cycle_title": "サイクルの更新",
"mentioned_comments_title": "メンション",
"mentioned_comments_subtitle": "誰かがコメントであなたをメンションしたとき。",
"new_comments_title": "新しいコメント",
"new_comments_subtitle": "フォローしているタスクに新しいコメントが追加されたとき。",
"reaction_comments_title": "リアクション",
"reaction_comments_subtitle": "誰かがあなたのコメントやタスクに絵文字で反応したときに通知を受け取ります。",
"setting_updated_successfully": "設定が正常に更新されました",
"failed_to_update_setting": "設定の更新に失敗しました"
}
}
+37 -26
View File
@@ -350,7 +350,7 @@
"couldnt_remove_the_project_from_favorites": "프로젝트를 즐겨찾기에서 제거하지 못했습니다. 다시 시도해주세요.",
"add_to_favorites": "즐겨찾기에 추가",
"remove_from_favorites": "즐겨찾기에서 제거",
"publish_project": "프로젝트 게시",
"publish_settings": "설정 게시",
"publish": "게시",
"copy_link": "링크 복사",
"leave_project": "프로젝트 떠나기",
@@ -871,8 +871,7 @@
"deleting": "삭제 중",
"pending": "보류 중",
"invite": "초대",
"view": "보기",
"deactivated_user": "비활성화된 사용자"
"view": "보기"
},
"chart": {
@@ -1743,15 +1742,12 @@
"title": "프로젝트 추정 활성화",
"description": "팀의 복잡성과 작업량을 전달하는 데 도움이 됩니다.",
"no_estimate": "추정 없음",
"new": "새 추정 시스템",
"create": {
"custom": "사용자 지정",
"start_from_scratch": "처음부터 시작",
"choose_template": "템플릿 선택",
"choose_estimate_system": "추정 시스템 선택",
"enter_estimate_point": "추정 입력",
"step": "단계 {step}/{total}",
"label": "추정 생성"
"enter_estimate_point": "추정 입력"
},
"toasts": {
"created": {
@@ -1800,25 +1796,6 @@
"already_exists": "추정 값이 이미 존재합니다.",
"unsaved_changes": "저장되지 않은 변경 사항이 있습니다. 완료를 클릭하기 전에 저장하세요",
"remove_empty": "추정은 비어있을 수 없습니다. 각 필드에 값을 입력하거나 값이 없는 필드를 제거하세요."
},
"systems": {
"points": {
"label": "포인트",
"fibonacci": "피보나치",
"linear": "선형",
"squares": "제곱",
"custom": "사용자 정의"
},
"categories": {
"label": "카테고리",
"t_shirt_sizes": "티셔츠 사이즈",
"easy_to_hard": "쉬움에서 어려움",
"custom": "사용자 정의"
},
"time": {
"label": "시간",
"hours": "시간"
}
}
},
"automations": {
@@ -2462,5 +2439,39 @@
"last_edited_by": "마지막 편집자",
"previously_edited_by": "이전 편집자",
"edited_by": "편집자"
},
"notification_settings": {
"page_label": "{workspace} - 받은 편지함 설정",
"inbox_settings": "받은 편지함 설정",
"inbox_settings_description": "작업 공간의 활동에 대한 알림을 받는 방법을 사용자 지정합니다. 변경 사항은 자동으로 저장됩니다.",
"advanced_settings": "고급 설정",
"in_plane": "Plane 내",
"email": "이메일",
"slack": "Slack",
"task_updates": "작업 항목 업데이트",
"task_updates_subtitle": "작업 공간의 작업 항목이 업데이트될 때 알림을 받습니다.",
"comments": "댓글",
"comments_subtitle": "작업 공간의 토론을 최신 상태로 유지합니다.",
"work_item_property_title": "작업 항목의 속성 업데이트",
"work_item_property_subtitle": "작업 공간의 작업 항목이 업데이트될 때 알림을 받습니다.",
"status_title": "상태 변경",
"status_subtitle": "작업 항목의 상태가 업데이트될 때.",
"priority_title": "우선순위 변경",
"priority_subtitle": "작업 항목의 우선순위 수준이 조정될 때.",
"assignee_title": "담당자 변경",
"assignee_subtitle": "작업 항목이 누군가에게 할당되거나 재할당될 때.",
"due_date_title": "날짜 변경",
"due_date_subtitle": "작업 항목의 시작일 또는 마감일이 업데이트될 때.",
"module_title": "모듈 업데이트",
"cycle_title": "사이클 업데이트",
"mentioned_comments_title": "멘션",
"mentioned_comments_subtitle": "누군가가 댓글에서 당신을 멘션할 때.",
"new_comments_title": "새 댓글",
"new_comments_subtitle": "팔로우 중인 작업에 새 댓글이 추가될 때.",
"reaction_comments_title": "반응",
"reaction_comments_subtitle": "누군가가 이모티콘으로 당신의 댓글이나 작업에 반응할 때 알림을 받습니다.",
"setting_updated_successfully": "설정이 성공적으로 업데이트되었습니다",
"failed_to_update_setting": "설정 업데이트 실패"
}
}
+37 -26
View File
@@ -348,7 +348,7 @@
"couldnt_remove_the_project_from_favorites": "Nie udało się usunąć projektu z ulubionych. Spróbuj ponownie.",
"add_to_favorites": "Dodaj do ulubionych",
"remove_from_favorites": "Usuń z ulubionych",
"publish_project": "Opublikuj projekt",
"publish_settings": "Ustawienia publikowania",
"publish": "Opublikuj",
"copy_link": "Kopiuj link",
"leave_project": "Opuść projekt",
@@ -865,8 +865,7 @@
"deleting": "Usuwanie",
"pending": "Oczekujące",
"invite": "Zaproś",
"view": "Widok",
"deactivated_user": "Dezaktywowany użytkownik"
"view": "Widok"
},
"chart": {
"x_axis": "Oś X",
@@ -1718,15 +1717,12 @@
"title": "Włącz szacunki dla mojego projektu",
"description": "Pomagają w komunikacji o złożoności i obciążeniu zespołu.",
"no_estimate": "Bez szacunku",
"new": "Nowy system szacowania",
"create": {
"custom": "Niestandardowy",
"start_from_scratch": "Zacznij od zera",
"choose_template": "Wybierz szablon",
"choose_estimate_system": "Wybierz system szacowania",
"enter_estimate_point": "Wprowadź punkt szacunkowy",
"step": "Krok {step} z {total}",
"label": "Utwórz szacunek"
"enter_estimate_point": "Wprowadź punkt szacunkowy"
},
"toasts": {
"created": {
@@ -1775,25 +1771,6 @@
"already_exists": "Wartość szacunku już istnieje.",
"unsaved_changes": "Masz niezapisane zmiany. Zapisz je przed kliknięciem 'gotowe'",
"remove_empty": "Szacunek nie może być pusty. Wprowadź wartość w każde pole lub usuń te, dla których nie masz wartości."
},
"systems": {
"points": {
"label": "Punkty",
"fibonacci": "Fibonacci",
"linear": "Liniowy",
"squares": "Kwadraty",
"custom": "Własny"
},
"categories": {
"label": "Kategorie",
"t_shirt_sizes": "Rozmiary koszulek",
"easy_to_hard": "Od łatwego do trudnego",
"custom": "Własne"
},
"time": {
"label": "Czas",
"hours": "Godziny"
}
}
},
"automations": {
@@ -2419,5 +2396,39 @@
"last_edited_by": "Ostatnio edytowane przez",
"previously_edited_by": "Wcześniej edytowane przez",
"edited_by": "Edytowane przez"
},
"notification_settings": {
"page_label": "{workspace} - Ustawienia skrzynki odbiorczej",
"inbox_settings": "Ustawienia skrzynki odbiorczej",
"inbox_settings_description": "Dostosuj sposób otrzymywania powiadomień o aktywnościach w swoim obszarze roboczym. Twoje zmiany są zapisywane automatycznie.",
"advanced_settings": "Zaawansowane ustawienia",
"in_plane": "W Plane",
"email": "Email",
"slack": "Slack",
"task_updates": "Aktualizacje elementów pracy",
"task_updates_subtitle": "Otrzymuj powiadomienia, gdy elementy pracy w twoim obszarze roboczym są aktualizowane.",
"comments": "Komentarze",
"comments_subtitle": "Bądź na bieżąco z dyskusjami w swoim obszarze roboczym.",
"work_item_property_title": "Aktualizacja dowolnej właściwości elementu pracy",
"work_item_property_subtitle": "Otrzymuj powiadomienia, gdy elementy pracy w twoim obszarze roboczym są aktualizowane.",
"status_title": "Zmiana stanu",
"status_subtitle": "Gdy stan elementu pracy jest aktualizowany.",
"priority_title": "Zmiana priorytetu",
"priority_subtitle": "Gdy poziom priorytetu elementu pracy jest dostosowywany.",
"assignee_title": "Zmiana przypisanego",
"assignee_subtitle": "Gdy element pracy jest przypisywany lub przypisywany ponownie komuś.",
"due_date_title": "Zmiana daty",
"due_date_subtitle": "Gdy data rozpoczęcia lub termin elementu pracy jest aktualizowany.",
"module_title": "Aktualizacja modułu",
"cycle_title": "Aktualizacja cyklu",
"mentioned_comments_title": "Wzmianki",
"mentioned_comments_subtitle": "Gdy ktoś wspomina cię w komentarzu.",
"new_comments_title": "Nowe komentarze",
"new_comments_subtitle": "Gdy nowy komentarz zostanie dodany do zadania, które śledzisz.",
"reaction_comments_title": "Reakcje",
"reaction_comments_subtitle": "Otrzymuj powiadomienia, gdy ktoś reaguje na twoje komentarze lub zadania za pomocą emotikony.",
"setting_updated_successfully": "Ustawienie zaktualizowane pomyślnie",
"failed_to_update_setting": "Nie udało się zaktualizować ustawienia"
}
}
@@ -350,7 +350,7 @@
"couldnt_remove_the_project_from_favorites": "Não foi possível remover o projeto dos favoritos. Por favor, tente novamente.",
"add_to_favorites": "Adicionar aos favoritos",
"remove_from_favorites": "Remover dos favoritos",
"publish_project": "Publicar projeto",
"publish_settings": "Configurações de publicação",
"publish": "Publicar",
"copy_link": "Copiar link",
"leave_project": "Sair do projeto",
@@ -871,8 +871,7 @@
"deleting": "Excluindo",
"pending": "Pendente",
"invite": "Convidar",
"view": "Visualizar",
"deactivated_user": "Usuário desativado"
"view": "Visualizar"
},
"chart": {
@@ -1743,15 +1742,12 @@
"title": "Habilitar estimativas para meu projeto",
"description": "Elas ajudam você a comunicar a complexidade e a carga de trabalho da equipe.",
"no_estimate": "Sem estimativa",
"new": "Novo sistema de estimativa",
"create": {
"custom": "Personalizado",
"start_from_scratch": "Começar do zero",
"choose_template": "Escolher um modelo",
"choose_estimate_system": "Escolher um sistema de estimativa",
"enter_estimate_point": "Inserir estimativa",
"step": "Passo {step} de {total}",
"label": "Criar estimativa"
"enter_estimate_point": "Inserir estimativa"
},
"toasts": {
"created": {
@@ -1800,25 +1796,6 @@
"already_exists": "O valor da estimativa já existe.",
"unsaved_changes": "Você tem algumas alterações não salvas. Por favor, salve-as antes de clicar em concluir",
"remove_empty": "A estimativa não pode estar vazia. Insira um valor em cada campo ou remova aqueles para os quais você não tem valores."
},
"systems": {
"points": {
"label": "Pontos",
"fibonacci": "Fibonacci",
"linear": "Linear",
"squares": "Quadrados",
"custom": "Personalizado"
},
"categories": {
"label": "Categorias",
"t_shirt_sizes": "Tamanhos de Camiseta",
"easy_to_hard": "Fácil a difícil",
"custom": "Personalizado"
},
"time": {
"label": "Tempo",
"hours": "Horas"
}
}
},
"automations": {
@@ -2455,5 +2432,39 @@
"last_edited_by": "Última edição por",
"previously_edited_by": "Anteriormente editado por",
"edited_by": "Editado por"
},
"notification_settings": {
"page_label": "{workspace} - Configurações da caixa de entrada",
"inbox_settings": "Configurações da caixa de entrada",
"inbox_settings_description": "Personalize como você recebe notificações para atividades no seu espaço de trabalho. Suas alterações são salvas automaticamente.",
"advanced_settings": "Configurações avançadas",
"in_plane": "No Plane",
"email": "Email",
"slack": "Slack",
"task_updates": "Atualizações de itens de trabalho",
"task_updates_subtitle": "Receba notificações quando os itens de trabalho no seu espaço de trabalho forem atualizados.",
"comments": "Comentários",
"comments_subtitle": "Fique atualizado sobre as discussões no seu espaço de trabalho.",
"work_item_property_title": "Atualização de qualquer propriedade do item de trabalho",
"work_item_property_subtitle": "Receba notificações quando os itens de trabalho no seu espaço de trabalho forem atualizados.",
"status_title": "Mudança de estado",
"status_subtitle": "Quando o estado de um item de trabalho é atualizado.",
"priority_title": "Mudança de prioridade",
"priority_subtitle": "Quando o nível de prioridade de um item de trabalho é ajustado.",
"assignee_title": "Mudança de responsável",
"assignee_subtitle": "Quando um item de trabalho é atribuído ou reatribuído a alguém.",
"due_date_title": "Mudança de data",
"due_date_subtitle": "Quando a data de início ou de vencimento de um item de trabalho é atualizada.",
"module_title": "Atualização de módulo",
"cycle_title": "Atualização de ciclo",
"mentioned_comments_title": "Menções",
"mentioned_comments_subtitle": "Quando alguém menciona você em um comentário.",
"new_comments_title": "Novos comentários",
"new_comments_subtitle": "Quando um novo comentário é adicionado a uma tarefa que você está seguindo.",
"reaction_comments_title": "Reações",
"reaction_comments_subtitle": "Receba notificações quando alguém reagir aos seus comentários ou tarefas com um emoji.",
"setting_updated_successfully": "Configuração atualizada com sucesso",
"failed_to_update_setting": "Falha ao atualizar a configuração"
}
}
+37 -26
View File
@@ -350,7 +350,7 @@
"couldnt_remove_the_project_from_favorites": "Nu s-a putut elimina proiectul din favorite. Încearcă din nou.",
"add_to_favorites": "Adaugă la favorite",
"remove_from_favorites": "Elimină din favorite",
"publish_project": "Publică proiectul",
"publish_settings": "Setări de publicare",
"publish": "Publică",
"copy_link": "Copiază link-ul",
"leave_project": "Părăsește proiectul",
@@ -869,8 +869,7 @@
"deleting": "Se șterge",
"pending": "În așteptare",
"invite": "Invită",
"view": "Vizualizează",
"deactivated_user": "Utilizator dezactivat"
"view": "Vizualizează"
},
"chart": {
@@ -1741,15 +1740,12 @@
"title": "Activează estimările pentru proiectul meu",
"description": "Te ajută să comunici complexitatea și volumul de muncă al echipei.",
"no_estimate": "Fără estimare",
"new": "Noul sistem de estimare",
"create": {
"custom": "Personalizat",
"start_from_scratch": "Începe de la zero",
"choose_template": "Alege un șablon",
"choose_estimate_system": "Alege un sistem de estimare",
"enter_estimate_point": "Introdu estimarea",
"step": "Pasul {step} de {total}",
"label": "Creează estimare"
"enter_estimate_point": "Introdu estimarea"
},
"toasts": {
"created": {
@@ -1798,25 +1794,6 @@
"already_exists": "Valoarea estimării există deja.",
"unsaved_changes": "Ai modificări nesalvate, te rugăm să le salvezi înainte de a finaliza",
"remove_empty": "Estimarea nu poate fi goală. Introdu o valoare în fiecare câmp sau elimină câmpurile pentru care nu ai valori."
},
"systems": {
"points": {
"label": "Puncte",
"fibonacci": "Fibonacci",
"linear": "Linear",
"squares": "Pătrate",
"custom": "Personalizat"
},
"categories": {
"label": "Categorii",
"t_shirt_sizes": "Mărimi tricou",
"easy_to_hard": "De la ușor la greu",
"custom": "Personalizat"
},
"time": {
"label": "Timp",
"hours": "Ore"
}
}
},
"automations": {
@@ -2454,5 +2431,39 @@
"last_edited_by": "Ultima editare de către",
"previously_edited_by": "Editat anterior de către",
"edited_by": "Editat de"
},
"notification_settings": {
"page_label": "{workspace} - Setări inbox",
"inbox_settings": "Setări inbox",
"inbox_settings_description": "Personalizează modul în care primești notificări pentru activitățile din spațiul tău de lucru. Modificările tale sunt salvate automat.",
"advanced_settings": "Setări avansate",
"in_plane": "În Plane",
"email": "Email",
"slack": "Slack",
"task_updates": "Actualizări ale elementelor de lucru",
"task_updates_subtitle": "Primește notificări când elementele de lucru din spațiul tău de lucru sunt actualizate.",
"comments": "Comentarii",
"comments_subtitle": "Rămâi la curent cu discuțiile din spațiul tău de lucru.",
"work_item_property_title": "Actualizare a oricărei proprietăți a elementului de lucru",
"work_item_property_subtitle": "Primește notificări când elementele de lucru din spațiul tău de lucru sunt actualizate.",
"status_title": "Schimbare de stare",
"status_subtitle": "Când starea unui element de lucru este actualizată.",
"priority_title": "Schimbare de prioritate",
"priority_subtitle": "Când nivelul de prioritate al unui element de lucru este ajustat.",
"assignee_title": "Schimbare de responsabil",
"assignee_subtitle": "Când un element de lucru este atribuit sau reatribuit cuiva.",
"due_date_title": "Schimbare de dată",
"due_date_subtitle": "Când data de început sau de finalizare a unui element de lucru este actualizată.",
"module_title": "Actualizare modul",
"cycle_title": "Actualizare ciclu",
"mentioned_comments_title": "Mențiuni",
"mentioned_comments_subtitle": "Când cineva te menționează într-un comentariu.",
"new_comments_title": "Comentarii noi",
"new_comments_subtitle": "Când un comentariu nou este adăugat la o sarcină pe care o urmărești.",
"reaction_comments_title": "Reacții",
"reaction_comments_subtitle": "Primește notificări când cineva reacționează la comentariile sau sarcinile tale cu un emoji.",
"setting_updated_successfully": "Setarea a fost actualizată cu succes",
"failed_to_update_setting": "Actualizarea setării a eșuat"
}
}
+37 -26
View File
@@ -348,7 +348,7 @@
"couldnt_remove_the_project_from_favorites": "Не удалось удалить проект из избранного. Попробуйте снова.",
"add_to_favorites": "Добавить в избранное",
"remove_from_favorites": "Удалить из избранного",
"publish_project": "Опубликовать проект",
"publish_settings": "Настройки публикации",
"publish": "Опубликовать",
"copy_link": "Копировать ссылку",
"leave_project": "Покинуть проект",
@@ -869,8 +869,7 @@
"deleting": "Удаление",
"pending": "Ожидание",
"invite": "Пригласить",
"view": "Просмотр",
"deactivated_user": "Деактивированный пользователь"
"view": "Просмотр"
},
"chart": {
@@ -1741,15 +1740,12 @@
"title": "Включить оценки для моего проекта",
"description": "Они помогают вам в общении о сложности и рабочей нагрузке команды.",
"no_estimate": "Без оценки",
"new": "Новая система оценок",
"create": {
"custom": "Пользовательская",
"start_from_scratch": "Начать с нуля",
"choose_template": "Выбрать шаблон",
"choose_estimate_system": "Выбрать систему оценок",
"enter_estimate_point": "Ввести оценку",
"step": "Шаг {step} из {total}",
"label": "Создать оценку"
"enter_estimate_point": "Ввести оценку"
},
"toasts": {
"created": {
@@ -1798,25 +1794,6 @@
"already_exists": "Значение оценки уже существует.",
"unsaved_changes": "У вас есть несохраненные изменения. Пожалуйста, сохраните их перед нажатием на готово",
"remove_empty": "Оценка не может быть пустой. Введите значение в каждое поле или удалите те, для которых у вас нет значений."
},
"systems": {
"points": {
"label": "Баллы",
"fibonacci": "Фибоначчи",
"linear": "Линейная",
"squares": "Квадраты",
"custom": "Пользовательская"
},
"categories": {
"label": "Категории",
"t_shirt_sizes": "Размеры футболок",
"easy_to_hard": "От простого к сложному",
"custom": "Пользовательская"
},
"time": {
"label": "Время",
"hours": "Часы"
}
}
},
"automations": {
@@ -2460,5 +2437,39 @@
"last_edited_by": "Последнее редактирование",
"previously_edited_by": "Ранее отредактировано",
"edited_by": "Отредактировано"
},
"notification_settings": {
"page_label": "{workspace} - Настройки входящих",
"inbox_settings": "Настройки входящих",
"inbox_settings_description": "Настройте, как вы получаете уведомления о действиях в вашем рабочем пространстве. Ваши изменения сохраняются автоматически.",
"advanced_settings": "Расширенные настройки",
"in_plane": "В Plane",
"email": "Электронная почта",
"slack": "Slack",
"task_updates": "Обновления рабочих элементов",
"task_updates_subtitle": "Получайте уведомления, когда рабочие элементы в вашем рабочем пространстве обновляются.",
"comments": "Комментарии",
"comments_subtitle": "Будьте в курсе обсуждений в вашем рабочем пространстве.",
"work_item_property_title": "Обновление любой свойства рабочего элемента",
"work_item_property_subtitle": "Получайте уведомления, когда рабочие элементы в вашем рабочем пространстве обновляются.",
"status_title": "Изменение состояния",
"status_subtitle": "Когда состояние рабочего элемента обновляется.",
"priority_title": "Изменение приоритета",
"priority_subtitle": "Когда уровень приоритета рабочего элемента изменяется.",
"assignee_title": "Изменение назначенного",
"assignee_subtitle": "Когда рабочий элемент назначается или переназначается кому-то.",
"due_date_title": "Изменение даты",
"due_date_subtitle": "Когда дата начала или срок выполнения рабочего элемента обновляется.",
"module_title": "Обновление модуля",
"cycle_title": "Обновление цикла",
"mentioned_comments_title": "Упоминания",
"mentioned_comments_subtitle": "Когда кто-то упоминает вас в комментарии.",
"new_comments_title": "Новые комментарии",
"new_comments_subtitle": "Когда новый комментарий добавляется к задаче, которую вы отслеживаете.",
"reaction_comments_title": "Реакции",
"reaction_comments_subtitle": "Получайте уведомления, когда кто-то реагирует на ваши комментарии или задачи с помощью эмодзи.",
"setting_updated_successfully": "Настройка успешно обновлена",
"failed_to_update_setting": "Не удалось обновить настройку"
}
}
+37 -26
View File
@@ -348,7 +348,7 @@
"couldnt_remove_the_project_from_favorites": "Nepodarilo sa odstrániť projekt z obľúbených. Skúste to prosím znova.",
"add_to_favorites": "Pridať do obľúbených",
"remove_from_favorites": "Odstrániť z obľúbených",
"publish_project": "Publikovať projekt",
"publish_settings": "Nastavenia publikovania",
"publish": "Publikovať",
"copy_link": "Kopírovať odkaz",
"leave_project": "Opustiť projekt",
@@ -869,8 +869,7 @@
"deleting": "Mazanie",
"pending": "Čakajúce",
"invite": "Pozvať",
"view": "Zobraziť",
"deactivated_user": "Deaktivovaný používateľ"
"view": "Zobraziť"
},
"chart": {
@@ -1740,15 +1739,12 @@
"title": "Povoliť odhady pre môj projekt",
"description": "Pomáhajú vám komunikovať zložitosť a pracovné zaťaženie tímu.",
"no_estimate": "Bez odhadu",
"new": "Nový systém odhadov",
"create": {
"custom": "Vlastné",
"start_from_scratch": "Začať od nuly",
"choose_template": "Vybrať šablónu",
"choose_estimate_system": "Vybrať systém odhadov",
"enter_estimate_point": "Zadať bod odhadu",
"step": "Krok {step} z {total}",
"label": "Vytvoriť odhad"
"enter_estimate_point": "Zadať bod odhadu"
},
"toasts": {
"created": {
@@ -1797,25 +1793,6 @@
"already_exists": "Hodnota odhadu už existuje.",
"unsaved_changes": "Máte neuložené zmeny. Prosím, uložte ich pred kliknutím na hotovo",
"remove_empty": "Odhad nemôže byť prázdny. Zadajte hodnotu do každého poľa alebo odstráňte prázdne polia."
},
"systems": {
"points": {
"label": "Body",
"fibonacci": "Fibonacci",
"linear": "Lineárne",
"squares": "Štvorce",
"custom": "Vlastné"
},
"categories": {
"label": "Kategórie",
"t_shirt_sizes": "Veľkosti tričiek",
"easy_to_hard": "Od jednoduchého po náročné",
"custom": "Vlastné"
},
"time": {
"label": "Čas",
"hours": "Hodiny"
}
}
},
"automations": {
@@ -2459,5 +2436,39 @@
"last_edited_by": "Naposledy upravené používateľom",
"previously_edited_by": "Predtým upravené používateľom",
"edited_by": "Upravené používateľom"
},
"notification_settings": {
"page_label": "{workspace} - Nastavenia doručenej pošty",
"inbox_settings": "Nastavenia doručenej pošty",
"inbox_settings_description": "Prispôsobte si, ako dostávate upozornenia na aktivity vo vašom pracovnom priestore. Vaše zmeny sa ukladajú automaticky.",
"advanced_settings": "Pokročilé nastavenia",
"in_plane": "V Plane",
"email": "Email",
"slack": "Slack",
"task_updates": "Aktualizácie pracovných položiek",
"task_updates_subtitle": "Dostávajte upozornenia, keď sa aktualizujú pracovné položky vo vašom pracovnom priestore.",
"comments": "Komentáre",
"comments_subtitle": "Buďte informovaní o diskusiách vo vašom pracovnom priestore.",
"work_item_property_title": "Aktualizácia akejkoľvek vlastnosti pracovnej položky",
"work_item_property_subtitle": "Dostávajte upozornenia, keď sa aktualizujú pracovné položky vo vašom pracovnom priestore.",
"status_title": "Zmena stavu",
"status_subtitle": "Keď sa aktualizuje stav pracovnej položky.",
"priority_title": "Zmena priority",
"priority_subtitle": "Keď sa upraví úroveň priority pracovnej položky.",
"assignee_title": "Zmena priradeného",
"assignee_subtitle": "Keď sa pracovná položka priradí alebo priradí niekomu inému.",
"due_date_title": "Zmena dátumu",
"due_date_subtitle": "Keď sa aktualizuje dátum začiatku alebo termín pracovnej položky.",
"module_title": "Aktualizácia modulu",
"cycle_title": "Aktualizácia cyklu",
"mentioned_comments_title": "Zmienenia",
"mentioned_comments_subtitle": "Keď vás niekto zmieni v komentári.",
"new_comments_title": "Nové komentáre",
"new_comments_subtitle": "Keď sa pridá nový komentár k úlohe, ktorú sledujete.",
"reaction_comments_title": "Reakcie",
"reaction_comments_subtitle": "Dostávajte upozornenia, keď niekto reaguje na vaše komentáre alebo úlohy pomocou emotikonu.",
"setting_updated_successfully": "Nastavenie bolo úspešne aktualizované",
"failed_to_update_setting": "Nepodarilo sa aktualizovať nastavenie"
}
}
File diff suppressed because it is too large Load Diff
+37 -26
View File
@@ -348,7 +348,7 @@
"couldnt_remove_the_project_from_favorites": "Не вдалося вилучити проєкт із вибраного. Спробуйте ще раз.",
"add_to_favorites": "Додати у вибране",
"remove_from_favorites": "Вилучити з вибраного",
"publish_project": "Опублікувати проєкт",
"publish_settings": "Налаштування публікації",
"publish": "Опублікувати",
"copy_link": "Скопіювати посилання",
"leave_project": "Вийти з проєкту",
@@ -864,8 +864,7 @@
"deleting": "Видалення",
"pending": "Очікує",
"invite": "Запросити",
"view": "Подання",
"deactivated_user": "Деактивований користувач"
"view": "Подання"
},
"chart": {
"x_axis": "Вісь X",
@@ -1717,15 +1716,12 @@
"title": "Увімкнути оцінки для мого проєкту",
"description": "Вони допомагають вам повідомляти про складність та навантаження команди.",
"no_estimate": "Без оцінки",
"new": "Нова система оцінок",
"create": {
"custom": "Власний",
"start_from_scratch": "Почати з нуля",
"choose_template": "Вибрати шаблон",
"choose_estimate_system": "Вибрати систему оцінок",
"enter_estimate_point": "Введіть оцінку",
"step": "Крок {step} з {total}",
"label": "Створити оцінку"
"enter_estimate_point": "Введіть оцінку"
},
"toasts": {
"created": {
@@ -1774,25 +1770,6 @@
"already_exists": "Таке значення оцінки вже існує.",
"unsaved_changes": "У вас є незбережені зміни. Збережіть їх перед тим, як натиснути 'готово'",
"remove_empty": "Оцінка не може бути порожньою. Введіть значення в кожне поле або видаліть ті, для яких у вас немає значень."
},
"systems": {
"points": {
"label": "Бали",
"fibonacci": "Фібоначчі",
"linear": "Лінійна",
"squares": "Квадрати",
"custom": "Власна"
},
"categories": {
"label": "Категорії",
"t_shirt_sizes": "Розміри футболок",
"easy_to_hard": "Від легкого до складного",
"custom": "Власна"
},
"time": {
"label": "Час",
"hours": "Години"
}
}
},
"automations": {
@@ -2418,5 +2395,39 @@
"last_edited_by": "Останнє редагування",
"previously_edited_by": "Раніше відредаговано",
"edited_by": "Відредаговано"
},
"notification_settings": {
"page_label": "{workspace} - Налаштування вхідних",
"inbox_settings": "Налаштування вхідних",
"inbox_settings_description": "Налаштуйте, як ви отримуєте сповіщення про активності у вашому робочому просторі. Ваші зміни зберігаються автоматично.",
"advanced_settings": "Розширені налаштування",
"in_plane": "У Plane",
"email": "Електронна пошта",
"slack": "Slack",
"task_updates": "Оновлення робочих елементів",
"task_updates_subtitle": "Отримуйте сповіщення, коли робочі елементи у вашому робочому просторі оновлюються.",
"comments": "Коментарі",
"comments_subtitle": "Будьте в курсі обговорень у вашому робочому просторі.",
"work_item_property_title": "Оновлення будь-якої властивості робочого елемента",
"work_item_property_subtitle": "Отримуйте сповіщення, коли робочі елементи у вашому робочому просторі оновлюються.",
"status_title": "Зміна стану",
"status_subtitle": "Коли стан робочого елемента оновлюється.",
"priority_title": "Зміна пріоритету",
"priority_subtitle": "Коли рівень пріоритету робочого елемента змінюється.",
"assignee_title": "Зміна відповідального",
"assignee_subtitle": "Коли робочий елемент призначається або перепризначається комусь.",
"due_date_title": "Зміна дати",
"due_date_subtitle": "Коли дата початку або термін виконання робочого елемента оновлюється.",
"module_title": "Оновлення модуля",
"cycle_title": "Оновлення циклу",
"mentioned_comments_title": "Згадки",
"mentioned_comments_subtitle": "Коли хтось згадує вас у коментарі.",
"new_comments_title": "Нові коментарі",
"new_comments_subtitle": "Коли додається новий коментар до завдання, яке ви відстежуєте.",
"reaction_comments_title": "Реакції",
"reaction_comments_subtitle": "Отримуйте сповіщення, коли хтось реагує на ваші коментарі або завдання за допомогою емодзі.",
"setting_updated_successfully": "Налаштування успішно оновлено",
"failed_to_update_setting": "Не вдалося оновити налаштування"
}
}
@@ -348,7 +348,7 @@
"couldnt_remove_the_project_from_favorites": "Không thể xóa dự án khỏi mục yêu thích. Vui lòng thử lại.",
"add_to_favorites": "Thêm vào mục yêu thích",
"remove_from_favorites": "Xóa khỏi mục yêu thích",
"publish_project": "Xuất bản dự án",
"publish_settings": "Cài đặt xuất bản",
"publish": "Xuất bản",
"copy_link": "Sao chép liên kết",
"leave_project": "Rời dự án",
@@ -863,8 +863,7 @@
"deleting": "Đang xóa",
"pending": "Đang chờ xử lý",
"invite": "Mời",
"view": "Xem",
"deactivated_user": "Người dùng bị vô hiệu hóa"
"view": "Xem"
},
"chart": {
"x_axis": "Trục X",
@@ -1716,15 +1715,12 @@
"title": "Bật ước tính cho dự án của tôi",
"description": "Chúng giúp bạn truyền đạt độ phức tạp và khối lượng công việc của nhóm.",
"no_estimate": "Không có ước tính",
"new": "Hệ thống ước tính mới",
"create": {
"custom": "Tùy chỉnh",
"start_from_scratch": "Bắt đầu từ đầu",
"choose_template": "Chọn mẫu",
"choose_estimate_system": "Chọn hệ thống ước tính",
"enter_estimate_point": "Nhập điểm ước tính",
"step": "Bước {step} của {total}",
"label": "Tạo ước tính"
"enter_estimate_point": "Nhập điểm ước tính"
},
"toasts": {
"created": {
@@ -1772,25 +1768,6 @@
"empty": "Giá trị ước tính không được để trống",
"already_exists": "Giá trị ước tính này đã tồn tại",
"unsaved_changes": "Bạn có thay đổi chưa lưu. Vui lòng lưu trước khi nhấn 'xong'"
},
"systems": {
"points": {
"label": "Điểm",
"fibonacci": "Fibonacci",
"linear": "Tuyến tính",
"squares": "Bình phương",
"custom": "Tùy chỉnh"
},
"categories": {
"label": "Danh mục",
"t_shirt_sizes": "Kích cỡ áo",
"easy_to_hard": "Dễ đến khó",
"custom": "Tùy chỉnh"
},
"time": {
"label": "Thời gian",
"hours": "Giờ"
}
}
},
"automations": {
@@ -2416,5 +2393,39 @@
"last_edited_by": "Chỉnh sửa lần cuối bởi",
"previously_edited_by": "Trước đây được chỉnh sửa bởi",
"edited_by": "Được chỉnh sửa bởi"
},
"notification_settings": {
"page_label": "{workspace} - Cài đặt hộp thư đến",
"inbox_settings": "Cài đặt hộp thư đến",
"inbox_settings_description": "Tùy chỉnh cách bạn nhận thông báo cho các hoạt động trong không gian làm việc của bạn. Các thay đổi của bạn được lưu tự động.",
"advanced_settings": "Cài đặt nâng cao",
"in_plane": "Trong Plane",
"email": "Email",
"slack": "Slack",
"task_updates": "Cập nhật mục công việc",
"task_updates_subtitle": "Nhận thông báo khi các mục công việc trong không gian làm việc của bạn được cập nhật.",
"comments": "Bình luận",
"comments_subtitle": "Cập nhật thông tin về các cuộc thảo luận trong không gian làm việc của bạn.",
"work_item_property_title": "Cập nhật bất kỳ thuộc tính nào của mục công việc",
"work_item_property_subtitle": "Nhận thông báo khi các mục công việc trong không gian làm việc của bạn được cập nhật.",
"status_title": "Thay đổi trạng thái",
"status_subtitle": "Khi trạng thái của một mục công việc được cập nhật.",
"priority_title": "Thay đổi ưu tiên",
"priority_subtitle": "Khi mức độ ưu tiên của một mục công việc được điều chỉnh.",
"assignee_title": "Thay đổi người được giao",
"assignee_subtitle": "Khi một mục công việc được giao hoặc giao lại cho ai đó.",
"due_date_title": "Thay đổi ngày",
"due_date_subtitle": "Khi ngày bắt đầu hoặc ngày đến hạn của một mục công việc được cập nhật.",
"module_title": "Cập nhật mô-đun",
"cycle_title": "Cập nhật chu kỳ",
"mentioned_comments_title": "Đề cập",
"mentioned_comments_subtitle": "Khi ai đó đề cập đến bạn trong một bình luận.",
"new_comments_title": "Bình luận mới",
"new_comments_subtitle": "Khi một bình luận mới được thêm vào một nhiệm vụ bạn đang theo dõi.",
"reaction_comments_title": "Phản ứng",
"reaction_comments_subtitle": "Nhận thông báo khi ai đó phản ứng với bình luận hoặc nhiệm vụ của bạn bằng một biểu tượng cảm xúc.",
"setting_updated_successfully": "Cài đặt đã được cập nhật thành công",
"failed_to_update_setting": "Không thể cập nhật cài đặt"
}
}
@@ -350,7 +350,7 @@
"couldnt_remove_the_project_from_favorites": "无法从收藏中移除项目。请重试。",
"add_to_favorites": "添加到收藏",
"remove_from_favorites": "从收藏中移除",
"publish_project": "发布项目",
"publish_settings": "发布设置",
"publish": "发布",
"copy_link": "复制链接",
"leave_project": "离开项目",
@@ -870,8 +870,7 @@
"deleting": "删除中",
"pending": "待处理",
"invite": "邀请",
"view": "查看",
"deactivated_user": "已停用用户"
"view": "查看"
},
"chart": {
@@ -1741,15 +1740,12 @@
"title": "为我的项目启用估算",
"description": "它们有助于您传达团队的复杂性和工作量。",
"no_estimate": "无估算",
"new": "新估算系统",
"create": {
"custom": "自定义",
"start_from_scratch": "从头开始",
"choose_template": "选择模板",
"choose_estimate_system": "选择估算系统",
"enter_estimate_point": "输入估算点数",
"step": "步骤 {step} 共 {total}",
"label": "创建估算"
"enter_estimate_point": "输入估算点数"
},
"toasts": {
"created": {
@@ -1788,16 +1784,6 @@
"message": "无法禁用估算。请重试"
}
}
},
"validation": {
"min_length": "估算需要大于0。",
"unable_to_process": "我们无法处理您的请求,请重试。",
"numeric": "估算需要是数值。",
"character": "估算需要是字符值。",
"empty": "估算值不能为空。",
"already_exists": "估算值已存在。",
"unsaved_changes": "您有未保存的更改,请在点击完成前保存。",
"remove_empty": "估算不能为空。请在每个字段中输入值或删除没有值的字段。"
}
},
"automations": {
@@ -2441,5 +2427,39 @@
"last_edited_by": "最后编辑者",
"previously_edited_by": "之前编辑者",
"edited_by": "编辑者"
},
"notification_settings": {
"page_label": "{workspace} - 收件箱设置",
"inbox_settings": "收件箱设置",
"inbox_settings_description": "自定义您如何接收工作区活动的通知。您的更改会自动保存。",
"advanced_settings": "高级设置",
"in_plane": "在 Plane",
"email": "电子邮件",
"slack": "Slack",
"task_updates": "任务更新",
"task_updates_subtitle": "当您工作区中的任务更新时收到通知。",
"comments": "评论",
"comments_subtitle": "随时了解您工作区中的讨论。",
"work_item_property_title": "工作项任何属性的更新",
"work_item_property_subtitle": "当您工作区中的工作项更新时获得通知。",
"status_title": "状态变更",
"status_subtitle": "当工作项的状态更新时。",
"priority_title": "优先级变更",
"priority_subtitle": "当工作项的优先级被调整时。",
"assignee_title": "负责人变更",
"assignee_subtitle": "当工作项被分配或重新分配给某人时。",
"due_date_title": "日期变更",
"due_date_subtitle": "当工作项的开始日期或截止日期被更新时。",
"module_title": "模块变更",
"cycle_title": "周期变更",
"mentioned_comments_title": "提及",
"mentioned_comments_subtitle": "当有人在评论中提及您时。",
"new_comments_title": "新评论",
"new_comments_subtitle": "当您关注的任务中添加新评论时。",
"reaction_comments_title": "反应",
"reaction_comments_subtitle": "当有人用表情符号对您的评论或任务做出反应时收到通知。",
"setting_updated_successfully": "设置已成功更新",
"failed_to_update_setting": "无法更新设置"
}
}
@@ -350,7 +350,7 @@
"couldnt_remove_the_project_from_favorites": "無法從我的最愛移除專案。請再試一次。",
"add_to_favorites": "加入我的最愛",
"remove_from_favorites": "從我的最愛移除",
"publish_project": "發佈專案",
"publish_settings": "發布設定",
"publish": "發布",
"copy_link": "複製連結",
"leave_project": "離開專案",
@@ -871,8 +871,7 @@
"deleting": "刪除中",
"pending": "待處理",
"invite": "邀請",
"view": "檢視",
"deactivated_user": "已停用用戶"
"view": "檢視"
},
"chart": {
@@ -1743,15 +1742,12 @@
"title": "為我的專案啟用預估",
"description": "幫助你傳達團隊的複雜性和工作負荷。",
"no_estimate": "無預估",
"new": "新估算系統",
"create": {
"custom": "自訂",
"start_from_scratch": "從頭開始",
"choose_template": "選擇範本",
"choose_estimate_system": "選擇預估系統",
"enter_estimate_point": "輸入預估",
"step": "步驟 {step} 共 {total}",
"label": "建立預估"
"enter_estimate_point": "輸入預估"
},
"toasts": {
"created": {
@@ -1800,25 +1796,6 @@
"already_exists": "預估值已存在。",
"unsaved_changes": "你有未儲存的變更。請在點擊完成前儲存",
"remove_empty": "預估不能為空。在每個欄位中輸入值或移除沒有值的欄位。"
},
"systems": {
"points": {
"label": "點數",
"fibonacci": "費波那契數列",
"linear": "線性",
"squares": "平方數",
"custom": "自訂"
},
"categories": {
"label": "類別",
"t_shirt_sizes": "T恤尺寸",
"easy_to_hard": "簡單到困難",
"custom": "自訂"
},
"time": {
"label": "時間",
"hours": "小時"
}
}
},
"automations": {
@@ -2462,5 +2439,39 @@
"last_edited_by": "最後編輯者",
"previously_edited_by": "先前編輯者",
"edited_by": "編輯者"
},
"notification_settings": {
"page_label": "{workspace} - 收件匣設定",
"inbox_settings": "收件匣設定",
"inbox_settings_description": "自訂您如何接收工作區活動的通知。您的更改會自動保存。",
"advanced_settings": "進階設定",
"in_plane": "在 Plane",
"email": "電子郵件",
"slack": "Slack",
"task_updates": "工作項更新",
"task_updates_subtitle": "當工作區中的工作項更新時收到通知。",
"comments": "評論",
"comments_subtitle": "隨時了解工作區中的討論。",
"work_item_property_title": "工作項的任何屬性更新",
"work_item_property_subtitle": "當工作區中的工作項更新時收到通知。",
"status_title": "狀態更改",
"status_subtitle": "當工作項的狀態更新時。",
"priority_title": "優先級更改",
"priority_subtitle": "當工作項的優先級級別調整時。",
"assignee_title": "負責人更改",
"assignee_subtitle": "當工作項被分配或重新分配給某人時。",
"due_date_title": "日期更改",
"due_date_subtitle": "當工作項的開始或截止日期更新時。",
"module_title": "模組更新",
"cycle_title": "週期更新",
"mentioned_comments_title": "提及",
"mentioned_comments_subtitle": "當有人在評論中提到您時。",
"new_comments_title": "新評論",
"new_comments_subtitle": "當您關注的任務中添加新評論時。",
"reaction_comments_title": "反應",
"reaction_comments_subtitle": "當有人用表情符號對您的評論或任務做出反應時收到通知。",
"setting_updated_successfully": "設定更新成功",
"failed_to_update_setting": "設定更新失敗"
}
}
-2
View File
@@ -173,8 +173,6 @@ export class TranslationStore {
return import("../locales/ro/translations.json");
case "vi-VN":
return import("../locales/vi-VN/translations.json");
case "tr-TR":
return import("../locales/tr-TR/translations.json");
default:
throw new Error(`Unsupported language: ${language}`);
}
+1 -2
View File
@@ -16,8 +16,7 @@ export type TLanguage =
| "pt-BR"
| "id"
| "ro"
| "vi-VN"
| "tr-TR";
| "vi-VN";
export interface ILanguageOption {
label: string;
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@plane/logger",
"version": "0.26.0",
"version": "0.25.3",
"license": "AGPL-3.0",
"description": "Logger shared across multiple apps internally",
"private": true,
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@plane/propel",
"version": "0.26.0",
"version": "0.25.3",
"private": true,
"license": "AGPL-3.0",
"scripts": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@plane/services",
"version": "0.26.0",
"version": "0.25.3",
"license": "AGPL-3.0",
"private": true,
"main": "./src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@plane/shared-state",
"version": "0.26.0",
"version": "0.25.3",
"license": "AGPL-3.0",
"description": "Shared state shared across multiple apps internally",
"private": true,
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@plane/tailwind-config",
"version": "0.26.0",
"version": "0.25.3",
"license": "AGPL-3.0",
"description": "common tailwind configuration across monorepo",
"main": "tailwind.config.js",
@@ -27,7 +27,6 @@ module.exports = {
theme: {
extend: {
boxShadow: {
"custom-shadow": "var(--color-shadow-custom)",
"custom-shadow-2xs": "var(--color-shadow-2xs)",
"custom-shadow-xs": "var(--color-shadow-xs)",
"custom-shadow-sm": "var(--color-shadow-sm)",
@@ -209,28 +208,6 @@ module.exports = {
hover: "rgba(96, 100, 108, 0.25)",
active: "rgba(96, 100, 108, 0.7)",
},
subscription: {
free: {
200: convertToRGB("--color-subscription-free-200"),
400: convertToRGB("--color-subscription-free-400"),
},
one: {
200: convertToRGB("--color-subscription-one-200"),
400: convertToRGB("--color-subscription-one-400"),
},
pro: {
200: convertToRGB("--color-subscription-pro-200"),
400: convertToRGB("--color-subscription-pro-400"),
},
business: {
200: convertToRGB("--color-subscription-business-200"),
400: convertToRGB("--color-subscription-business-400"),
},
enterprise: {
200: convertToRGB("--color-subscription-enterprise-200"),
400: convertToRGB("--color-subscription-enterprise-400"),
},
},
},
onboarding: {
background: {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@plane/types",
"version": "0.26.0",
"version": "0.25.3",
"license": "AGPL-3.0",
"private": true,
"types": "./src/index.d.ts",
+7 -4
View File
@@ -14,7 +14,10 @@ export interface IEstimatePoint {
updated_by: string | undefined;
}
export type TEstimateSystemKeys = EEstimateSystem.POINTS | EEstimateSystem.CATEGORIES | EEstimateSystem.TIME;
export type TEstimateSystemKeys =
| EEstimateSystem.POINTS
| EEstimateSystem.CATEGORIES
| EEstimateSystem.TIME;
export interface IEstimate {
id: string | undefined;
@@ -52,14 +55,12 @@ export type TEstimatePointsObject = {
export type TTemplateValues = {
title: string;
i18n_title: string;
values: TEstimatePointsObject[];
hide?: boolean;
};
export type TEstimateSystem = {
name: string;
i18n_name: string;
templates: Record<string, TTemplateValues>;
is_available: boolean;
is_ee: boolean;
@@ -81,4 +82,6 @@ export type TEstimateTypeErrorObject = {
message: string | undefined;
};
export type TEstimateTypeError = Record<number, TEstimateTypeErrorObject> | undefined;
export type TEstimateTypeError =
| Record<number, TEstimateTypeErrorObject>
| undefined;
-1
View File
@@ -35,7 +35,6 @@ export type TIssueEntityData = {
sequence_id: number;
project_id: string;
project_identifier: string;
is_epic: boolean;
};
export type TActivityEntityData = {
+1 -1
View File
@@ -41,5 +41,5 @@ export * from "./epics";
export * from "./charts";
export * from "./home";
export * from "./stickies";
export * from "./notification";
export * from "./utils";
export * from "./payment";
+1 -4
View File
@@ -1,6 +1,3 @@
// plane imports
import { EInboxIssueSource } from "@plane/constants";
// local imports
import {
TIssueActivityWorkspaceDetail,
TIssueActivityProjectDetail,
@@ -34,7 +31,7 @@ export type TIssueActivity = {
epoch: number;
issue_comment: string | null;
source_data: {
source: EInboxIssueSource;
source: "IN_APP" | "FORM" | "EMAIL";
source_email?: string;
extra: {
username?: string;
+1 -1
View File
@@ -40,7 +40,7 @@ export type TIssueComment = {
};
export type TCommentsOperations = {
createComment: (data: Partial<TIssueComment>) => Promise<Partial<TIssueComment> | undefined>;
createComment: (data: Partial<TIssueComment>) => Promise<void>;
updateComment: (commentId: string, data: Partial<TIssueComment>) => Promise<void>;
removeComment: (commentId: string) => Promise<void>;
uploadCommentAsset: (blockId: string, file: File, commentId?: string) => Promise<TFileSignedURLResponse>;
+21
View File
@@ -0,0 +1,21 @@
import { ENotificationSettingsKey, EWorkspaceNotificationTransport } from "@plane/constants";
export type TNotificationSettings = {
i18n_title: string,
i18n_subtitle?: string,
key: ENotificationSettingsKey
}
export type TWorkspaceUserNotification = {
workspace: string,
user: string,
transport: EWorkspaceNotificationTransport,
property_change: boolean,
state_change: boolean,
priority: boolean,
assignee: boolean,
start_due_date: boolean,
comment: boolean,
mention: boolean,
comment_reactions: boolean
}
-36
View File
@@ -1,36 +0,0 @@
import { EProductSubscriptionEnum } from "@plane/constants";
export type TBillingFrequency = "month" | "year";
export type IPaymentProductPrice = {
currency: string;
id: string;
product: string;
recurring: TBillingFrequency;
unit_amount: number;
workspace_amount: number;
};
export type TProductSubscriptionType = "FREE" | "ONE" | "PRO" | "BUSINESS" | "ENTERPRISE";
export type IPaymentProduct = {
description: string;
id: string;
name: string;
type: Omit<TProductSubscriptionType, "FREE">;
payment_quantity: number;
prices: IPaymentProductPrice[];
is_active: boolean;
};
export type TSubscriptionPrice = {
key: string;
id: string | undefined;
currency: string;
price: number;
recurring: TBillingFrequency;
};
export type TProductBillingFrequency = {
[key in EProductSubscriptionEnum]: TBillingFrequency | undefined;
};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@plane/typescript-config",
"version": "0.26.0",
"version": "0.25.3",
"license": "AGPL-3.0",
"private": true,
"files": [
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "@plane/ui",
"description": "UI components shared across multiple apps internally",
"private": true,
"version": "0.26.0",
"version": "0.25.3",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
+1 -1
View File
@@ -42,7 +42,7 @@ export const DropdownOptions: React.FC<IMultiSelectDropdownOptions | ISingleSele
isMobile={isMobile}
/>
)}
<div className={cn("max-h-48 space-y-1 overflow-y-scroll", !disableSearch && "mt-2")}>
<div className="mt-2 max-h-48 space-y-1 overflow-y-scroll">
<>
{options ? (
options.length > 0 ? (
@@ -2,12 +2,12 @@ import React, { useEffect, useRef, useState } from "react";
import ReactDOM from "react-dom";
// plane helpers
import { useOutsideClickDetector } from "@plane/hooks";
// components
import { ContextMenuItem } from "./item";
// helpers
import { cn } from "../../../helpers";
// hooks
import { usePlatformOS } from "../../hooks/use-platform-os";
// components
import { ContextMenuItem } from "./item";
export type TContextMenuItem = {
key: string;
+4 -4
View File
@@ -1,14 +1,14 @@
import { Menu } from "@headlessui/react";
import { ChevronDown, MoreHorizontal } from "lucide-react";
import * as React from "react";
import ReactDOM from "react-dom";
import { Menu } from "@headlessui/react";
import { usePopper } from "react-popper";
import { ChevronDown, MoreHorizontal } from "lucide-react";
// plane helpers
import { useOutsideClickDetector } from "@plane/hooks";
// helpers
import { cn } from "../../helpers";
// hooks
import { useDropdownKeyDown } from "../hooks/use-dropdown-key-down";
// helpers
import { cn } from "../../helpers";
// types
import { ICustomMenuDropdownProps, ICustomMenuItemProps } from "./helper";
-1
View File
@@ -32,4 +32,3 @@ export * from "./tag";
export * from "./tabs";
export * from "./calendar";
export * from "./color-picker";
export * from "./link";
-69
View File
@@ -1,69 +0,0 @@
import React, { FC } from "react";
// plane utils
import { calculateTimeAgo, cn, getIconForLink } from "@plane/utils";
// plane ui
import { TContextMenuItem } from "../dropdowns/context-menu/root";
import { CustomMenu } from "../dropdowns/custom-menu";
export type TLinkItemBlockProps = {
title: string;
url: string;
createdAt?: Date | string;
menuItems?: TContextMenuItem[];
onClick?: () => void;
};
export const LinkItemBlock: FC<TLinkItemBlockProps> = (props) => {
// props
const { title, url, createdAt, menuItems, onClick } = props;
// icons
const Icon = getIconForLink(url);
return (
<div
onClick={onClick}
className="cursor-pointer group flex items-center bg-custom-background-100 px-4 w-[230px] h-[56px] border-[0.5px] border-custom-border-200 rounded-md gap-4"
>
<div className="flex-shrink-0 size-8 rounded p-2 bg-custom-background-90 grid place-items-center">
<Icon className="size-4 stroke-2 text-custom-text-350 group-hover:text-custom-text-100" />
</div>
<div className="flex-1 truncate">
<div className="text-sm font-medium truncate">{title}</div>
{createdAt && <div className="text-xs font-medium text-custom-text-400">{calculateTimeAgo(createdAt)}</div>}
</div>
{menuItems && (
<div className="hidden group-hover:block">
<CustomMenu placement="bottom-end" menuItemsClassName="z-20" closeOnSelect verticalEllipsis>
{menuItems.map((item) => (
<CustomMenu.MenuItem
key={item.key}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
item.action();
}}
className={cn("flex items-center gap-2 w-full ", {
"text-custom-text-400": item.disabled,
})}
disabled={item.disabled}
>
{item.icon && <item.icon className={cn("h-3 w-3", item.iconClassName)} />}
<div>
<h5>{item.title}</h5>
{item.description && (
<p
className={cn("text-custom-text-300 whitespace-pre-line", {
"text-custom-text-400": item.disabled,
})}
>
{item.description}
</p>
)}
</div>
</CustomMenu.MenuItem>
))}
</CustomMenu>
</div>
)}
</div>
);
};

Some files were not shown because too many files have changed in this diff Show More