diff --git a/.github/workflows/build-aio-branch.yml b/.github/workflows/build-aio-branch.yml index a3a2f35fa..3cc288354 100644 --- a/.github/workflows/build-aio-branch.yml +++ b/.github/workflows/build-aio-branch.yml @@ -88,7 +88,7 @@ jobs: full_build_push: if: ${{ needs.branch_build_setup.outputs.do_full_build == 'true' }} - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: [branch_build_setup] env: BUILD_TYPE: full @@ -148,7 +148,7 @@ jobs: slim_build_push: if: ${{ needs.branch_build_setup.outputs.do_slim_build == 'true' }} - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: [branch_build_setup] env: BUILD_TYPE: slim diff --git a/.github/workflows/build-branch.yml b/.github/workflows/build-branch.yml index 3599fc71e..d9ba3afee 100644 --- a/.github/workflows/build-branch.yml +++ b/.github/workflows/build-branch.yml @@ -299,32 +299,6 @@ jobs: buildx-platforms: ${{ needs.branch_build_setup.outputs.gh_buildx_platforms }} buildx-endpoint: ${{ needs.branch_build_setup.outputs.gh_buildx_endpoint }} - attach_assets_to_build: - if: ${{ needs.branch_build_setup.outputs.build_type == 'Release' }} - name: Attach Assets to Release - runs-on: ubuntu-22.04 - needs: [branch_build_setup] - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Update Assets - run: | - cp ./deploy/selfhost/install.sh deploy/selfhost/setup.sh - - - name: Attach Assets - id: attach_assets - uses: actions/upload-artifact@v4 - with: - name: selfhost-assets - retention-days: 2 - path: | - ${{ github.workspace }}/deploy/selfhost/setup.sh - ${{ github.workspace }}/deploy/selfhost/swarm.sh - ${{ github.workspace }}/deploy/selfhost/restore.sh - ${{ github.workspace }}/deploy/selfhost/docker-compose.yml - ${{ github.workspace }}/deploy/selfhost/variables.env - publish_release: if: ${{ needs.branch_build_setup.outputs.build_type == 'Release' }} name: Build Release @@ -338,7 +312,6 @@ jobs: branch_build_push_live, branch_build_push_apiserver, branch_build_push_proxy, - attach_assets_to_build, ] env: REL_VERSION: ${{ needs.branch_build_setup.outputs.release_version }} @@ -349,6 +322,8 @@ jobs: - name: Update Assets run: | cp ./deploy/selfhost/install.sh deploy/selfhost/setup.sh + sed -i 's/${APP_RELEASE:-stable}/${APP_RELEASE:-'${REL_VERSION}'}/g' deploy/selfhost/docker-compose.yml + sed -i 's/APP_RELEASE=stable/APP_RELEASE='${REL_VERSION}'/g' deploy/selfhost/variables.env - name: Create Release id: create_release diff --git a/.github/workflows/feature-deployment.yml b/.github/workflows/feature-deployment.yml index 311014cf8..dad3489df 100644 --- a/.github/workflows/feature-deployment.yml +++ b/.github/workflows/feature-deployment.yml @@ -51,7 +51,7 @@ jobs: uses: actions/checkout@v4 full_build_push: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: [branch_build_setup] env: BUILD_TYPE: full diff --git a/.github/workflows/sync-repo.yml b/.github/workflows/sync-repo.yml index 1f13995de..5d6c72cb7 100644 --- a/.github/workflows/sync-repo.yml +++ b/.github/workflows/sync-repo.yml @@ -11,7 +11,7 @@ env: jobs: sync_changes: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 permissions: pull-requests: write contents: read diff --git a/admin/package.json b/admin/package.json index 440916ad2..12642cd0f 100644 --- a/admin/package.json +++ b/admin/package.json @@ -1,6 +1,8 @@ { "name": "admin", - "version": "0.25.0", + "description": "Admin UI for Plane", + "version": "0.25.1", + "license": "AGPL-3.0", "private": true, "scripts": { "dev": "turbo run develop", diff --git a/apiserver/package.json b/apiserver/package.json index 38f397890..06e514d9e 100644 --- a/apiserver/package.json +++ b/apiserver/package.json @@ -1,4 +1,7 @@ { "name": "plane-api", - "version": "0.25.0" + "version": "0.25.1", + "license": "AGPL-3.0", + "private": true, + "description": "API server powering Plane's backend" } diff --git a/apiserver/plane/api/serializers/issue.py b/apiserver/plane/api/serializers/issue.py index 275ebeb07..82969efe7 100644 --- a/apiserver/plane/api/serializers/issue.py +++ b/apiserver/plane/api/serializers/issue.py @@ -80,6 +80,7 @@ class IssueSerializer(BaseSerializer): data["assignees"] = ProjectMember.objects.filter( project_id=self.context.get("project_id"), is_active=True, + role__gte=15, member_id__in=data["assignees"], ).values_list("member_id", flat=True) @@ -158,8 +159,13 @@ class IssueSerializer(BaseSerializer): pass else: try: - # Then assign it to default assignee - if default_assignee_id is not None: + # Then assign it to default assignee, if it is a valid assignee + if default_assignee_id is not None and ProjectMember.objects.filter( + member_id=default_assignee_id, + project_id=project_id, + role__gte=15, + is_active=True + ).exists(): IssueAssignee.objects.create( assignee_id=default_assignee_id, issue=issue, diff --git a/apiserver/plane/app/serializers/__init__.py b/apiserver/plane/app/serializers/__init__.py index 479f08d5a..623071573 100644 --- a/apiserver/plane/app/serializers/__init__.py +++ b/apiserver/plane/app/serializers/__init__.py @@ -121,8 +121,6 @@ from .exporter import ExporterHistorySerializer from .webhook import WebhookSerializer, WebhookLogSerializer -from .dashboard import DashboardSerializer, WidgetSerializer - from .favorite import UserFavoriteSerializer from .draft import ( diff --git a/apiserver/plane/app/serializers/dashboard.py b/apiserver/plane/app/serializers/dashboard.py deleted file mode 100644 index d09fc3e35..000000000 --- a/apiserver/plane/app/serializers/dashboard.py +++ /dev/null @@ -1,21 +0,0 @@ -# Module imports -from .base import BaseSerializer -from plane.db.models import DeprecatedDashboard, DeprecatedWidget - -# Third party frameworks -from rest_framework import serializers - - -class DashboardSerializer(BaseSerializer): - class Meta: - model = DeprecatedDashboard - fields = "__all__" - - -class WidgetSerializer(BaseSerializer): - is_visible = serializers.BooleanField(read_only=True) - widget_filters = serializers.JSONField(read_only=True) - - class Meta: - model = DeprecatedWidget - fields = ["id", "key", "is_visible", "widget_filters"] diff --git a/apiserver/plane/app/serializers/issue.py b/apiserver/plane/app/serializers/issue.py index 2fb7c035d..1b754ea6a 100644 --- a/apiserver/plane/app/serializers/issue.py +++ b/apiserver/plane/app/serializers/issue.py @@ -36,6 +36,7 @@ from plane.db.models import ( State, IssueVersion, IssueDescriptionVersion, + ProjectMember, ) @@ -119,6 +120,17 @@ class IssueCreateSerializer(BaseSerializer): raise serializers.ValidationError("Start date cannot exceed target date") return data + def get_valid_assignees(self, assignees, project_id): + if not assignees: + return [] + + return ProjectMember.objects.filter( + project_id=project_id, + role__gte=15, + is_active=True, + member_id__in=assignees + ).values_list('member_id', flat=True) + def create(self, validated_data): assignees = validated_data.pop("assignee_ids", None) labels = validated_data.pop("label_ids", None) @@ -134,27 +146,33 @@ class IssueCreateSerializer(BaseSerializer): created_by_id = issue.created_by_id updated_by_id = issue.updated_by_id - if assignees is not None and len(assignees): + valid_assignee_ids = self.get_valid_assignees(assignees, project_id) + if valid_assignee_ids is not None and len(valid_assignee_ids): try: IssueAssignee.objects.bulk_create( [ IssueAssignee( - assignee=user, + assignee_id=user_id, issue=issue, project_id=project_id, workspace_id=workspace_id, created_by_id=created_by_id, updated_by_id=updated_by_id, ) - for user in assignees + for user_id in valid_assignee_ids ], batch_size=10, ) except IntegrityError: pass else: - # Then assign it to default assignee - if default_assignee_id is not None: + # Then assign it to default assignee, if it is a valid assignee + if default_assignee_id is not None and ProjectMember.objects.filter( + member_id=default_assignee_id, + project_id=project_id, + role__gte=15, + is_active=True + ).exists(): try: IssueAssignee.objects.create( assignee_id=default_assignee_id, @@ -198,20 +216,21 @@ class IssueCreateSerializer(BaseSerializer): created_by_id = instance.created_by_id updated_by_id = instance.updated_by_id - if assignees is not None: + valid_assignee_ids = self.get_valid_assignees(assignees, project_id) + if valid_assignee_ids is not None: IssueAssignee.objects.filter(issue=instance).delete() try: IssueAssignee.objects.bulk_create( [ IssueAssignee( - assignee=user, + assignee_id=user_id, issue=instance, project_id=project_id, workspace_id=workspace_id, created_by_id=created_by_id, updated_by_id=updated_by_id, ) - for user in assignees + for user_id in valid_assignee_ids ], batch_size=10, ignore_conflicts=True, diff --git a/apiserver/plane/app/urls/__init__.py b/apiserver/plane/app/urls/__init__.py index 3be75536b..8cfc18dbb 100644 --- a/apiserver/plane/app/urls/__init__.py +++ b/apiserver/plane/app/urls/__init__.py @@ -2,7 +2,6 @@ from .analytic import urlpatterns as analytic_urls from .api import urlpatterns as api_urls from .asset import urlpatterns as asset_urls from .cycle import urlpatterns as cycle_urls -from .dashboard import urlpatterns as dashboard_urls from .estimate import urlpatterns as estimate_urls from .external import urlpatterns as external_urls from .intake import urlpatterns as intake_urls @@ -23,7 +22,6 @@ urlpatterns = [ *analytic_urls, *asset_urls, *cycle_urls, - *dashboard_urls, *estimate_urls, *external_urls, *intake_urls, diff --git a/apiserver/plane/app/urls/dashboard.py b/apiserver/plane/app/urls/dashboard.py deleted file mode 100644 index 0dc24a808..000000000 --- a/apiserver/plane/app/urls/dashboard.py +++ /dev/null @@ -1,23 +0,0 @@ -from django.urls import path - - -from plane.app.views import DashboardEndpoint, WidgetsEndpoint - - -urlpatterns = [ - path( - "workspaces//dashboard/", - DashboardEndpoint.as_view(), - name="dashboard", - ), - path( - "workspaces//dashboard//", - DashboardEndpoint.as_view(), - name="dashboard", - ), - path( - "dashboard//widgets//", - WidgetsEndpoint.as_view(), - name="widgets", - ), -] diff --git a/apiserver/plane/app/views/__init__.py b/apiserver/plane/app/views/__init__.py index 684179d90..ba63920f6 100644 --- a/apiserver/plane/app/views/__init__.py +++ b/apiserver/plane/app/views/__init__.py @@ -210,8 +210,6 @@ from .webhook.base import ( WebhookSecretRegenerateEndpoint, ) -from .dashboard.base import DashboardEndpoint, WidgetsEndpoint - from .error_404 import custom_404_view from .notification.base import MarkAllReadNotificationViewSet diff --git a/apiserver/plane/app/views/dashboard/base.py b/apiserver/plane/app/views/dashboard/base.py deleted file mode 100644 index ff67bb73b..000000000 --- a/apiserver/plane/app/views/dashboard/base.py +++ /dev/null @@ -1,812 +0,0 @@ -# Django imports -from django.contrib.postgres.aggregates import ArrayAgg -from django.contrib.postgres.fields import ArrayField -from django.db.models import ( - Case, - CharField, - Count, - Exists, - F, - Func, - IntegerField, - JSONField, - OuterRef, - Prefetch, - Q, - Subquery, - UUIDField, - Value, - When, -) -from django.db.models.functions import Coalesce -from django.utils import timezone -from rest_framework import status - -# Third Party imports -from rest_framework.response import Response - -from plane.app.serializers import ( - DashboardSerializer, - IssueActivitySerializer, - IssueSerializer, - WidgetSerializer, -) -from plane.db.models import ( - DeprecatedDashboard, - DeprecatedDashboardWidget, - Issue, - IssueActivity, - FileAsset, - IssueLink, - IssueRelation, - Project, - DeprecatedWidget, - WorkspaceMember, - CycleIssue, -) -from plane.utils.issue_filters import issue_filters - -# Module imports -from .. import BaseAPIView - - -def dashboard_overview_stats(self, request, slug): - assigned_issues = ( - Issue.issue_objects.filter( - (Q(assignees__in=[request.user]) & Q(issue_assignee__deleted_at__isnull=True)), - project__project_projectmember__is_active=True, - project__project_projectmember__member=request.user, - workspace__slug=slug, - ) - .filter( - Q( - project__project_projectmember__role=5, - project__guest_view_all_features=True, - ) - | Q( - project__project_projectmember__role=5, - project__guest_view_all_features=False, - created_by=self.request.user, - ) - | - # For other roles (role < 5), show all issues - Q(project__project_projectmember__role__gt=5), - project__project_projectmember__member=self.request.user, - project__project_projectmember__is_active=True, - ) - .count() - ) - - pending_issues_count = ( - Issue.issue_objects.filter( - ~Q(state__group__in=["completed", "cancelled"]), - target_date__lt=timezone.now().date(), - project__project_projectmember__is_active=True, - project__project_projectmember__member=request.user, - workspace__slug=slug, - assignees__in=[request.user], - ) - .filter( - Q( - project__project_projectmember__role=5, - project__guest_view_all_features=True, - ) - | Q( - project__project_projectmember__role=5, - project__guest_view_all_features=False, - created_by=self.request.user, - ) - | - # For other roles (role < 5), show all issues - Q(project__project_projectmember__role__gt=5), - project__project_projectmember__member=self.request.user, - project__project_projectmember__is_active=True, - ) - .count() - ) - - created_issues_count = ( - Issue.issue_objects.filter( - workspace__slug=slug, - project__project_projectmember__is_active=True, - project__project_projectmember__member=request.user, - created_by_id=request.user.id, - ) - .filter( - Q( - project__project_projectmember__role=5, - project__guest_view_all_features=True, - ) - | Q( - project__project_projectmember__role=5, - project__guest_view_all_features=False, - created_by=self.request.user, - ) - | - # For other roles (role < 5), show all issues - Q(project__project_projectmember__role__gt=5), - project__project_projectmember__member=self.request.user, - project__project_projectmember__is_active=True, - ) - .count() - ) - - completed_issues_count = ( - Issue.issue_objects.filter( - ( - Q(assignees__in=[request.user]) - & Q(issue_assignee__deleted_at__isnull=True) - ), - workspace__slug=slug, - project__project_projectmember__is_active=True, - project__project_projectmember__member=request.user, - state__group="completed", - ) - .filter( - Q( - project__project_projectmember__role=5, - project__guest_view_all_features=True, - ) - | Q( - project__project_projectmember__role=5, - project__guest_view_all_features=False, - created_by=self.request.user, - ) - | - # For other roles (role < 5), show all issues - Q(project__project_projectmember__role__gt=5), - project__project_projectmember__member=self.request.user, - project__project_projectmember__is_active=True, - ) - .count() - ) - - return Response( - { - "assigned_issues_count": assigned_issues, - "pending_issues_count": pending_issues_count, - "completed_issues_count": completed_issues_count, - "created_issues_count": created_issues_count, - }, - status=status.HTTP_200_OK, - ) - - -def dashboard_assigned_issues(self, request, slug): - filters = issue_filters(request.query_params, "GET") - issue_type = request.GET.get("issue_type", None) - - # get all the assigned issues - assigned_issues = ( - Issue.issue_objects.filter( - ( - Q(assignees__in=[request.user]) - & Q(issue_assignee__deleted_at__isnull=True) - ), - workspace__slug=slug, - project__project_projectmember__member=request.user, - project__project_projectmember__is_active=True, - ) - .filter(**filters) - .select_related("workspace", "project", "state", "parent") - .prefetch_related("assignees", "labels", "issue_module__module") - .prefetch_related( - Prefetch( - "issue_relation", - queryset=IssueRelation.objects.select_related( - "related_issue" - ).select_related("issue"), - ) - ) - .annotate( - cycle_id=Subquery( - CycleIssue.objects.filter( - issue=OuterRef("id"), deleted_at__isnull=True - ).values("cycle_id")[:1] - ) - ) - .annotate( - link_count=IssueLink.objects.filter(issue=OuterRef("id")) - .order_by() - .annotate(count=Func(F("id"), function="Count")) - .values("count") - ) - .annotate( - attachment_count=FileAsset.objects.filter( - issue_id=OuterRef("id"), - entity_type=FileAsset.EntityTypeContext.ISSUE_ATTACHMENT, - ) - .order_by() - .annotate(count=Func(F("id"), function="Count")) - .values("count") - ) - .annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) - .order_by() - .annotate(count=Func(F("id"), function="Count")) - .values("count") - ) - .annotate( - label_ids=Coalesce( - ArrayAgg( - "labels__id", - distinct=True, - filter=Q( - ~Q(labels__id__isnull=True) - & Q(label_issue__deleted_at__isnull=True) - ), - ), - Value([], output_field=ArrayField(UUIDField())), - ), - assignee_ids=Coalesce( - ArrayAgg( - "assignees__id", - distinct=True, - filter=Q( - ~Q(assignees__id__isnull=True) - & Q(assignees__member_project__is_active=True) - & Q(issue_assignee__deleted_at__isnull=True) - ), - ), - Value([], output_field=ArrayField(UUIDField())), - ), - module_ids=Coalesce( - ArrayAgg( - "issue_module__module_id", - distinct=True, - filter=Q( - ~Q(issue_module__module_id__isnull=True) - & Q(issue_module__module__archived_at__isnull=True) - & Q(issue_module__deleted_at__isnull=True) - ), - ), - Value([], output_field=ArrayField(UUIDField())), - ), - ) - ) - - if WorkspaceMember.objects.filter( - workspace__slug=slug, member=request.user, role=5, is_active=True - ).exists(): - assigned_issues = assigned_issues.filter(created_by=request.user) - - # Priority Ordering - priority_order = ["urgent", "high", "medium", "low", "none"] - assigned_issues = assigned_issues.annotate( - priority_order=Case( - *[When(priority=p, then=Value(i)) for i, p in enumerate(priority_order)], - output_field=CharField(), - ) - ).order_by("priority_order") - - if issue_type == "pending": - pending_issues_count = assigned_issues.filter( - state__group__in=["backlog", "started", "unstarted"] - ).count() - pending_issues = assigned_issues.filter( - state__group__in=["backlog", "started", "unstarted"] - )[:5] - return Response( - { - "issues": IssueSerializer( - pending_issues, many=True, expand=self.expand - ).data, - "count": pending_issues_count, - }, - status=status.HTTP_200_OK, - ) - - if issue_type == "completed": - completed_issues_count = assigned_issues.filter( - state__group__in=["completed"] - ).count() - completed_issues = assigned_issues.filter(state__group__in=["completed"])[:5] - return Response( - { - "issues": IssueSerializer( - completed_issues, many=True, expand=self.expand - ).data, - "count": completed_issues_count, - }, - status=status.HTTP_200_OK, - ) - - if issue_type == "overdue": - overdue_issues_count = assigned_issues.filter( - state__group__in=["backlog", "unstarted", "started"], - target_date__lt=timezone.now(), - ).count() - overdue_issues = assigned_issues.filter( - state__group__in=["backlog", "unstarted", "started"], - target_date__lt=timezone.now(), - )[:5] - return Response( - { - "issues": IssueSerializer( - overdue_issues, many=True, expand=self.expand - ).data, - "count": overdue_issues_count, - }, - status=status.HTTP_200_OK, - ) - - if issue_type == "upcoming": - upcoming_issues_count = assigned_issues.filter( - state__group__in=["backlog", "unstarted", "started"], - target_date__gte=timezone.now(), - ).count() - upcoming_issues = assigned_issues.filter( - state__group__in=["backlog", "unstarted", "started"], - target_date__gte=timezone.now(), - )[:5] - return Response( - { - "issues": IssueSerializer( - upcoming_issues, many=True, expand=self.expand - ).data, - "count": upcoming_issues_count, - }, - status=status.HTTP_200_OK, - ) - - return Response( - {"error": "Please specify a valid issue type"}, - status=status.HTTP_400_BAD_REQUEST, - ) - - -def dashboard_created_issues(self, request, slug): - filters = issue_filters(request.query_params, "GET") - issue_type = request.GET.get("issue_type", None) - - # get all the assigned issues - created_issues = ( - Issue.issue_objects.filter( - workspace__slug=slug, - project__project_projectmember__member=request.user, - project__project_projectmember__is_active=True, - created_by=request.user, - ) - .filter(**filters) - .select_related("workspace", "project", "state", "parent") - .prefetch_related("assignees", "labels", "issue_module__module") - .annotate( - cycle_id=Subquery( - CycleIssue.objects.filter( - issue=OuterRef("id"), deleted_at__isnull=True - ).values("cycle_id")[:1] - ) - ) - .annotate( - link_count=IssueLink.objects.filter(issue=OuterRef("id")) - .order_by() - .annotate(count=Func(F("id"), function="Count")) - .values("count") - ) - .annotate( - attachment_count=FileAsset.objects.filter( - issue_id=OuterRef("id"), - entity_type=FileAsset.EntityTypeContext.ISSUE_ATTACHMENT, - ) - .order_by() - .annotate(count=Func(F("id"), function="Count")) - .values("count") - ) - .annotate( - sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) - .order_by() - .annotate(count=Func(F("id"), function="Count")) - .values("count") - ) - .annotate( - label_ids=Coalesce( - ArrayAgg( - "labels__id", - distinct=True, - filter=Q( - ~Q(labels__id__isnull=True) - & Q(label_issue__deleted_at__isnull=True) - ), - ), - Value([], output_field=ArrayField(UUIDField())), - ), - assignee_ids=Coalesce( - ArrayAgg( - "assignees__id", - distinct=True, - filter=Q( - ~Q(assignees__id__isnull=True) - & Q(assignees__member_project__is_active=True) - & Q(issue_assignee__deleted_at__isnull=True) - ), - ), - Value([], output_field=ArrayField(UUIDField())), - ), - module_ids=Coalesce( - ArrayAgg( - "issue_module__module_id", - distinct=True, - filter=Q( - ~Q(issue_module__module_id__isnull=True) - & Q(issue_module__module__archived_at__isnull=True) - & Q(issue_module__deleted_at__isnull=True) - ), - ), - Value([], output_field=ArrayField(UUIDField())), - ), - ) - .order_by("created_at") - ) - - # Priority Ordering - priority_order = ["urgent", "high", "medium", "low", "none"] - created_issues = created_issues.annotate( - priority_order=Case( - *[When(priority=p, then=Value(i)) for i, p in enumerate(priority_order)], - output_field=CharField(), - ) - ).order_by("priority_order") - - if issue_type == "pending": - pending_issues_count = created_issues.filter( - state__group__in=["backlog", "started", "unstarted"] - ).count() - pending_issues = created_issues.filter( - state__group__in=["backlog", "started", "unstarted"] - )[:5] - return Response( - { - "issues": IssueSerializer( - pending_issues, many=True, expand=self.expand - ).data, - "count": pending_issues_count, - }, - status=status.HTTP_200_OK, - ) - - if issue_type == "completed": - completed_issues_count = created_issues.filter( - state__group__in=["completed"] - ).count() - completed_issues = created_issues.filter(state__group__in=["completed"])[:5] - return Response( - { - "issues": IssueSerializer(completed_issues, many=True).data, - "count": completed_issues_count, - }, - status=status.HTTP_200_OK, - ) - - if issue_type == "overdue": - overdue_issues_count = created_issues.filter( - state__group__in=["backlog", "unstarted", "started"], - target_date__lt=timezone.now(), - ).count() - overdue_issues = created_issues.filter( - state__group__in=["backlog", "unstarted", "started"], - target_date__lt=timezone.now(), - )[:5] - return Response( - { - "issues": IssueSerializer(overdue_issues, many=True).data, - "count": overdue_issues_count, - }, - status=status.HTTP_200_OK, - ) - - if issue_type == "upcoming": - upcoming_issues_count = created_issues.filter( - state__group__in=["backlog", "unstarted", "started"], - target_date__gte=timezone.now(), - ).count() - upcoming_issues = created_issues.filter( - state__group__in=["backlog", "unstarted", "started"], - target_date__gte=timezone.now(), - )[:5] - return Response( - { - "issues": IssueSerializer(upcoming_issues, many=True).data, - "count": upcoming_issues_count, - }, - status=status.HTTP_200_OK, - ) - - return Response( - {"error": "Please specify a valid issue type"}, - status=status.HTTP_400_BAD_REQUEST, - ) - - -def dashboard_issues_by_state_groups(self, request, slug): - filters = issue_filters(request.query_params, "GET") - state_order = ["backlog", "unstarted", "started", "completed", "cancelled"] - extra_filters = {} - - if WorkspaceMember.objects.filter( - workspace__slug=slug, member=request.user, role=5, is_active=True - ).exists(): - extra_filters = {"created_by": request.user} - - issues_by_state_groups = ( - Issue.issue_objects.filter( - workspace__slug=slug, - project__project_projectmember__is_active=True, - project__project_projectmember__member=request.user, - assignees__in=[request.user], - ) - .filter(**filters, **extra_filters) - .values("state__group") - .annotate(count=Count("id")) - ) - - # default state - all_groups = {state: 0 for state in state_order} - - # Update counts for existing groups - for entry in issues_by_state_groups: - all_groups[entry["state__group"]] = entry["count"] - - # Prepare output including all groups with their counts - output_data = [ - {"state": group, "count": count} for group, count in all_groups.items() - ] - - return Response(output_data, status=status.HTTP_200_OK) - - -def dashboard_issues_by_priority(self, request, slug): - filters = issue_filters(request.query_params, "GET") - priority_order = ["urgent", "high", "medium", "low", "none"] - extra_filters = {} - - if WorkspaceMember.objects.filter( - workspace__slug=slug, member=request.user, role=5, is_active=True - ).exists(): - extra_filters = {"created_by": request.user} - - issues_by_priority = ( - Issue.issue_objects.filter( - workspace__slug=slug, - project__project_projectmember__is_active=True, - project__project_projectmember__member=request.user, - assignees__in=[request.user], - ) - .filter(**filters, **extra_filters) - .values("priority") - .annotate(count=Count("id")) - ) - - # default priority - all_groups = {priority: 0 for priority in priority_order} - - # Update counts for existing groups - for entry in issues_by_priority: - all_groups[entry["priority"]] = entry["count"] - - # Prepare output including all groups with their counts - output_data = [ - {"priority": group, "count": count} for group, count in all_groups.items() - ] - - return Response(output_data, status=status.HTTP_200_OK) - - -def dashboard_recent_activity(self, request, slug): - queryset = IssueActivity.objects.filter( - ~Q(field__in=["comment", "vote", "reaction", "draft"]), - workspace__slug=slug, - project__project_projectmember__member=request.user, - project__project_projectmember__is_active=True, - project__archived_at__isnull=True, - actor=request.user, - ).select_related("actor", "workspace", "issue", "project")[:8] - - return Response( - IssueActivitySerializer(queryset, many=True).data, status=status.HTTP_200_OK - ) - - -def dashboard_recent_projects(self, request, slug): - project_ids = ( - IssueActivity.objects.filter( - workspace__slug=slug, - project__project_projectmember__member=request.user, - project__project_projectmember__is_active=True, - project__archived_at__isnull=True, - actor=request.user, - ) - .values_list("project_id", flat=True) - .distinct() - ) - - # Extract project IDs from the recent projects - unique_project_ids = set(project_id for project_id in project_ids) - - # Fetch additional projects only if needed - if len(unique_project_ids) < 4: - additional_projects = Project.objects.filter( - project_projectmember__member=request.user, - project_projectmember__is_active=True, - archived_at__isnull=True, - workspace__slug=slug, - ).exclude(id__in=unique_project_ids) - - # Append additional project IDs to the existing list - unique_project_ids.update(additional_projects.values_list("id", flat=True)) - - return Response(list(unique_project_ids)[:4], status=status.HTTP_200_OK) - - -def dashboard_recent_collaborators(self, request, slug): - project_members_with_activities = ( - WorkspaceMember.objects.filter(workspace__slug=slug, is_active=True) - .annotate( - active_issue_count=Count( - Case( - When( - member__issue_assignee__issue__state__group__in=[ - "unstarted", - "started", - ], - member__issue_assignee__issue__workspace__slug=slug, - member__issue_assignee__issue__project__project_projectmember__member=request.user, - member__issue_assignee__issue__project__project_projectmember__is_active=True, - then=F("member__issue_assignee__issue__id"), - ), - distinct=True, - output_field=IntegerField(), - ), - distinct=True, - ), - user_id=F("member_id"), - ) - .values("user_id", "active_issue_count") - .order_by("-active_issue_count") - .distinct() - ) - return Response((project_members_with_activities), status=status.HTTP_200_OK) - - -class DashboardEndpoint(BaseAPIView): - def create(self, request, slug): - serializer = DashboardSerializer(data=request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status=status.HTTP_201_CREATED) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def patch(self, request, slug, pk): - serializer = DashboardSerializer(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) - - def delete(self, request, slug, pk): - serializer = DashboardSerializer(data=request.data, partial=True) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status=status.HTTP_204_NO_CONTENT) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def get(self, request, slug, dashboard_id=None): - if not dashboard_id: - dashboard_type = request.GET.get("dashboard_type", None) - if dashboard_type == "home": - dashboard, created = DeprecatedDashboard.objects.get_or_create( - type_identifier=dashboard_type, - owned_by=request.user, - is_default=True, - ) - - if created: - widgets_to_fetch = [ - "overview_stats", - "assigned_issues", - "created_issues", - "issues_by_state_groups", - "issues_by_priority", - "recent_activity", - "recent_projects", - "recent_collaborators", - ] - - updated_dashboard_widgets = [] - for widget_key in widgets_to_fetch: - widget = DeprecatedWidget.objects.filter( - key=widget_key - ).values_list("id", flat=True) - if widget: - updated_dashboard_widgets.append( - DeprecatedDashboardWidget( - widget_id=widget, dashboard_id=dashboard.id - ) - ) - - DeprecatedDashboardWidget.objects.bulk_create( - updated_dashboard_widgets, batch_size=100 - ) - - widgets = ( - DeprecatedWidget.objects.annotate( - is_visible=Exists( - DeprecatedDashboardWidget.objects.filter( - widget_id=OuterRef("pk"), - dashboard_id=dashboard.id, - is_visible=True, - ) - ) - ) - .annotate( - dashboard_filters=Subquery( - DeprecatedDashboardWidget.objects.filter( - widget_id=OuterRef("pk"), - dashboard_id=dashboard.id, - filters__isnull=False, - ) - .exclude(filters={}) - .values("filters")[:1] - ) - ) - .annotate( - widget_filters=Case( - When( - dashboard_filters__isnull=False, - then=F("dashboard_filters"), - ), - default=F("filters"), - output_field=JSONField(), - ) - ) - ) - return Response( - { - "dashboard": DashboardSerializer(dashboard).data, - "widgets": WidgetSerializer(widgets, many=True).data, - }, - status=status.HTTP_200_OK, - ) - return Response( - {"error": "Please specify a valid dashboard type"}, - status=status.HTTP_400_BAD_REQUEST, - ) - - widget_key = request.GET.get("widget_key", "overview_stats") - - WIDGETS_MAPPER = { - "overview_stats": dashboard_overview_stats, - "assigned_issues": dashboard_assigned_issues, - "created_issues": dashboard_created_issues, - "issues_by_state_groups": dashboard_issues_by_state_groups, - "issues_by_priority": dashboard_issues_by_priority, - "recent_activity": dashboard_recent_activity, - "recent_projects": dashboard_recent_projects, - "recent_collaborators": dashboard_recent_collaborators, - } - - func = WIDGETS_MAPPER.get(widget_key) - if func is not None: - response = func(self, request=request, slug=slug) - if isinstance(response, Response): - return response - - return Response( - {"error": "Please specify a valid widget key"}, - status=status.HTTP_400_BAD_REQUEST, - ) - - -class WidgetsEndpoint(BaseAPIView): - def patch(self, request, dashboard_id, widget_id): - dashboard_widget = DeprecatedDashboardWidget.objects.filter( - widget_id=widget_id, dashboard_id=dashboard_id - ).first() - dashboard_widget.is_visible = request.data.get( - "is_visible", dashboard_widget.is_visible - ) - dashboard_widget.sort_order = request.data.get( - "sort_order", dashboard_widget.sort_order - ) - dashboard_widget.filters = request.data.get("filters", dashboard_widget.filters) - dashboard_widget.save() - return Response({"message": "successfully updated"}, status=status.HTTP_200_OK) diff --git a/apiserver/plane/bgtasks/issue_activities_task.py b/apiserver/plane/bgtasks/issue_activities_task.py index dacb39236..efc7ce8da 100644 --- a/apiserver/plane/bgtasks/issue_activities_task.py +++ b/apiserver/plane/bgtasks/issue_activities_task.py @@ -34,6 +34,7 @@ from plane.bgtasks.webhook_task import webhook_activity from plane.utils.issue_relation_mapper import get_inverse_relation from plane.utils.valid_uuid import is_valid_uuid + # Track Changes in name def track_name( requested_data, @@ -852,7 +853,7 @@ def delete_cycle_issue_activity( issues = requested_data.get("issues") for issue in issues: current_issue = Issue.objects.filter(pk=issue).first() - if issue: + if current_issue: current_issue.updated_at = timezone.now() current_issue.save(update_fields=["updated_at"]) issue_activities.append( @@ -1569,13 +1570,12 @@ def issue_activity( issue_activities = [] # check if project_id is valid - if not is_valid_uuid(project_id): + if not is_valid_uuid(str(project_id)): return project = Project.objects.get(pk=project_id) workspace_id = project.workspace_id - if issue_id is not None: if origin: ri = redis_instance() diff --git a/apiserver/plane/db/migrations/0092_alter_deprecateddashboardwidget_unique_together_and_more.py b/apiserver/plane/db/migrations/0092_alter_deprecateddashboardwidget_unique_together_and_more.py new file mode 100644 index 000000000..86369bbdc --- /dev/null +++ b/apiserver/plane/db/migrations/0092_alter_deprecateddashboardwidget_unique_together_and_more.py @@ -0,0 +1,41 @@ +# Generated by Django 4.2.18 on 2025-02-25 15:48 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("db", "0091_issuecomment_edited_at_and_more"), + ] + + operations = [ + migrations.AlterUniqueTogether( + name="deprecateddashboardwidget", + unique_together=None, + ), + migrations.RemoveField( + model_name="deprecateddashboardwidget", + name="created_by", + ), + migrations.RemoveField( + model_name="deprecateddashboardwidget", + name="dashboard", + ), + migrations.RemoveField( + model_name="deprecateddashboardwidget", + name="updated_by", + ), + migrations.RemoveField( + model_name="deprecateddashboardwidget", + name="widget", + ), + migrations.DeleteModel( + name="DeprecatedDashboard", + ), + migrations.DeleteModel( + name="DeprecatedDashboardWidget", + ), + migrations.DeleteModel( + name="DeprecatedWidget", + ), + ] diff --git a/apiserver/plane/db/models/__init__.py b/apiserver/plane/db/models/__init__.py index 8e20a8d67..3cf46c919 100644 --- a/apiserver/plane/db/models/__init__.py +++ b/apiserver/plane/db/models/__init__.py @@ -3,7 +3,6 @@ from .api import APIActivityLog, APIToken from .asset import FileAsset from .base import BaseModel from .cycle import Cycle, CycleIssue, CycleUserProperties -from .dashboard import DeprecatedDashboard, DeprecatedDashboardWidget, DeprecatedWidget from .deploy_board import DeployBoard from .draft import ( DraftIssue, diff --git a/apiserver/plane/db/models/dashboard.py b/apiserver/plane/db/models/dashboard.py deleted file mode 100644 index d1101e352..000000000 --- a/apiserver/plane/db/models/dashboard.py +++ /dev/null @@ -1,92 +0,0 @@ -import uuid - -# Django imports -from django.db import models - -# Module imports -from ..mixins import TimeAuditModel -from .base import BaseModel - - -class DeprecatedDashboard(BaseModel): - DASHBOARD_CHOICES = ( - ("workspace", "Workspace"), - ("project", "Project"), - ("home", "Home"), - ("team", "Team"), - ("user", "User"), - ) - name = models.CharField(max_length=255) - description_html = models.TextField(blank=True, default="

") - identifier = models.UUIDField(null=True) - owned_by = models.ForeignKey( - "db.User", on_delete=models.CASCADE, related_name="dashboards" - ) - is_default = models.BooleanField(default=False) - type_identifier = models.CharField( - max_length=30, - choices=DASHBOARD_CHOICES, - verbose_name="Dashboard Type", - default="home", - ) - logo_props = models.JSONField(default=dict) - - def __str__(self): - """Return name of the dashboard""" - return f"{self.name}" - - class Meta: - verbose_name = "DeprecatedDashboard" - verbose_name_plural = "DeprecatedDashboards" - db_table = "deprecated_dashboards" - ordering = ("-created_at",) - - -class DeprecatedWidget(TimeAuditModel): - id = models.UUIDField( - default=uuid.uuid4, unique=True, editable=False, db_index=True, primary_key=True - ) - key = models.CharField(max_length=255) - filters = models.JSONField(default=dict) - logo_props = models.JSONField(default=dict) - - def __str__(self): - """Return name of the widget""" - return f"{self.key}" - - class Meta: - verbose_name = "DeprecatedWidget" - verbose_name_plural = "DeprecatedWidgets" - db_table = "deprecated_widgets" - ordering = ("-created_at",) - - -class DeprecatedDashboardWidget(BaseModel): - widget = models.ForeignKey( - DeprecatedWidget, on_delete=models.CASCADE, related_name="dashboard_widgets" - ) - dashboard = models.ForeignKey( - DeprecatedDashboard, on_delete=models.CASCADE, related_name="dashboard_widgets" - ) - is_visible = models.BooleanField(default=True) - sort_order = models.FloatField(default=65535) - filters = models.JSONField(default=dict) - properties = models.JSONField(default=dict) - - def __str__(self): - """Return name of the dashboard""" - return f"{self.dashboard.name} {self.widget.key}" - - class Meta: - unique_together = ("widget", "dashboard", "deleted_at") - constraints = [ - models.UniqueConstraint( - fields=["widget", "dashboard"], - condition=models.Q(deleted_at__isnull=True), - name="dashboard_widget_unique_widget_dashboard_when_deleted_at_null", - ) - ] - verbose_name = "Deprecated Dashboard Widget" - verbose_name_plural = "Deprecated Dashboard Widgets" - db_table = "deprecated_dashboard_widgets" - ordering = ("-created_at",) diff --git a/live/package.json b/live/package.json index a9f023af0..939cb8e94 100644 --- a/live/package.json +++ b/live/package.json @@ -1,7 +1,8 @@ { "name": "live", - "version": "0.25.0", - "description": "", + "version": "0.25.1", + "license": "AGPL-3.0", + "description": "A realtime collaborative server powers Plane's rich text editor", "main": "./src/server.ts", "private": true, "type": "module", @@ -14,7 +15,6 @@ }, "keywords": [], "author": "", - "license": "ISC", "dependencies": { "@hocuspocus/extension-database": "^2.15.0", "@hocuspocus/extension-logger": "^2.15.0", diff --git a/package.json b/package.json index dcbd9e288..0ae40ed8e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,8 @@ { + "name": "plane", + "description": "Open-source project management that unlocks customer value", "repository": "https://github.com/makeplane/plane.git", - "version": "0.25.0", + "version": "0.25.1", "license": "AGPL-3.0", "private": true, "workspaces": [ @@ -28,6 +30,5 @@ "nanoid": "3.3.8", "esbuild": "0.25.0" }, - "packageManager": "yarn@1.22.22", - "name": "plane" + "packageManager": "yarn@1.22.22" } diff --git a/packages/constants/package.json b/packages/constants/package.json index 715e21760..de8c02261 100644 --- a/packages/constants/package.json +++ b/packages/constants/package.json @@ -1,6 +1,7 @@ { "name": "@plane/constants", - "version": "0.25.0", + "version": "0.25.1", "private": true, - "main": "./src/index.ts" + "main": "./src/index.ts", + "license": "AGPL-3.0" } diff --git a/packages/editor/package.json b/packages/editor/package.json index 267d70cf3..cc4b519e5 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,7 +1,8 @@ { "name": "@plane/editor", - "version": "0.25.0", + "version": "0.25.1", "description": "Core Editor that powers Plane", + "license": "AGPL-3.0", "private": true, "main": "./dist/index.mjs", "module": "./dist/index.mjs", diff --git a/packages/editor/src/core/components/editors/editor-container.tsx b/packages/editor/src/core/components/editors/editor-container.tsx index d6563f7b0..b742a5265 100644 --- a/packages/editor/src/core/components/editors/editor-container.tsx +++ b/packages/editor/src/core/components/editors/editor-container.tsx @@ -1,5 +1,5 @@ -import { FC, ReactNode } from "react"; import { Editor } from "@tiptap/react"; +import { FC, ReactNode } from "react"; // plane utils import { cn } from "@plane/utils"; // constants @@ -71,7 +71,7 @@ export const EditorContainer: FC = (props) => { onClick={handleContainerClick} onMouseLeave={handleContainerMouseLeave} className={cn( - "editor-container cursor-text relative", + `editor-container cursor-text relative line-spacing-${displayConfig.lineSpacing ?? DEFAULT_DISPLAY_CONFIG.lineSpacing}`, { "active-editor": editor?.isFocused && editor?.isEditable, }, diff --git a/packages/editor/src/core/constants/config.ts b/packages/editor/src/core/constants/config.ts index bd4712de9..788454f96 100644 --- a/packages/editor/src/core/constants/config.ts +++ b/packages/editor/src/core/constants/config.ts @@ -4,6 +4,7 @@ import { TDisplayConfig } from "@/types"; export const DEFAULT_DISPLAY_CONFIG: TDisplayConfig = { fontSize: "large-font", fontStyle: "sans-serif", + lineSpacing: "regular", }; export const ACCEPTED_FILE_MIME_TYPES = ["image/jpeg", "image/jpg", "image/png", "image/webp", "image/gif"]; diff --git a/packages/editor/src/core/extensions/core-without-props.ts b/packages/editor/src/core/extensions/core-without-props.ts index 8864f49f7..9bad7a5c7 100644 --- a/packages/editor/src/core/extensions/core-without-props.ts +++ b/packages/editor/src/core/extensions/core-without-props.ts @@ -26,12 +26,12 @@ export const CoreEditorExtensionsWithoutProps = [ StarterKit.configure({ bulletList: { HTMLAttributes: { - class: "list-disc pl-7 space-y-2", + class: "list-disc pl-7 space-y-[--list-spacing-y]", }, }, orderedList: { HTMLAttributes: { - class: "list-decimal pl-7 space-y-2", + class: "list-decimal pl-7 space-y-[--list-spacing-y]", }, }, listItem: { diff --git a/packages/editor/src/core/extensions/extensions.tsx b/packages/editor/src/core/extensions/extensions.tsx index f04ed756a..485269e82 100644 --- a/packages/editor/src/core/extensions/extensions.tsx +++ b/packages/editor/src/core/extensions/extensions.tsx @@ -55,12 +55,12 @@ export const CoreEditorExtensions = (args: TArguments): Extensions => { StarterKit.configure({ bulletList: { HTMLAttributes: { - class: "list-disc pl-7 space-y-2", + class: "list-disc pl-7 space-y-[--list-spacing-y]", }, }, orderedList: { HTMLAttributes: { - class: "list-decimal pl-7 space-y-2", + class: "list-decimal pl-7 space-y-[--list-spacing-y]", }, }, listItem: { diff --git a/packages/editor/src/core/extensions/read-only-extensions.tsx b/packages/editor/src/core/extensions/read-only-extensions.tsx index 8711b2cb3..eb5256657 100644 --- a/packages/editor/src/core/extensions/read-only-extensions.tsx +++ b/packages/editor/src/core/extensions/read-only-extensions.tsx @@ -46,12 +46,12 @@ export const CoreReadOnlyEditorExtensions = (props: Props): Extensions => { StarterKit.configure({ bulletList: { HTMLAttributes: { - class: "list-disc pl-7 space-y-2", + class: "list-disc pl-7 space-y-[--list-spacing-y]", }, }, orderedList: { HTMLAttributes: { - class: "list-decimal pl-7 space-y-2", + class: "list-decimal pl-7 space-y-[--list-spacing-y]", }, }, listItem: { diff --git a/packages/editor/src/core/types/config.ts b/packages/editor/src/core/types/config.ts index d4d8ca901..12df0aa42 100644 --- a/packages/editor/src/core/types/config.ts +++ b/packages/editor/src/core/types/config.ts @@ -23,7 +23,10 @@ export type TEditorFontStyle = "sans-serif" | "serif" | "monospace"; export type TEditorFontSize = "small-font" | "large-font"; +export type TEditorLineSpacing = "regular" | "small"; + export type TDisplayConfig = { fontStyle?: TEditorFontStyle; fontSize?: TEditorFontSize; + lineSpacing?: TEditorLineSpacing; }; diff --git a/packages/editor/src/styles/editor.css b/packages/editor/src/styles/editor.css index 69bb61961..be686a5cc 100644 --- a/packages/editor/src/styles/editor.css +++ b/packages/editor/src/styles/editor.css @@ -252,7 +252,7 @@ ul[data-type="taskList"] li[data-checked="true"] { div[data-type="horizontalRule"] { line-height: 0; - padding: 0.25rem 0; + padding: var(--divider-padding-top) 0 var(--divider-padding-bottom) 0; margin-top: 0; margin-bottom: 0; @@ -335,10 +335,10 @@ p.editor-paragraph-block { /* tailwind typography */ .prose :where(h1.editor-heading-block):not(:where([class~="not-prose"], [class~="not-prose"] *)) { &:not(:first-child) { - padding-top: 28px; + padding-top: var(--heading-1-padding-top); } - padding-bottom: 4px; + padding-bottom: var(--heading-1-padding-bottom); font-size: var(--font-size-h1); line-height: var(--line-height-h1); font-weight: 600; @@ -346,10 +346,10 @@ p.editor-paragraph-block { .prose :where(h2.editor-heading-block):not(:where([class~="not-prose"], [class~="not-prose"] *)) { &:not(:first-child) { - padding-top: 28px; + padding-top: var(--heading-2-padding-top); } - padding-bottom: 4px; + padding-bottom: var(--heading-2-padding-bottom); font-size: var(--font-size-h2); line-height: var(--line-height-h2); font-weight: 600; @@ -357,10 +357,10 @@ p.editor-paragraph-block { .prose :where(h3.editor-heading-block):not(:where([class~="not-prose"], [class~="not-prose"] *)) { &:not(:first-child) { - padding-top: 28px; + padding-top: var(--heading-3-padding-top); } - padding-bottom: 4px; + padding-bottom: var(--heading-3-padding-bottom); font-size: var(--font-size-h3); line-height: var(--line-height-h3); font-weight: 600; @@ -368,10 +368,10 @@ p.editor-paragraph-block { .prose :where(h4.editor-heading-block):not(:where([class~="not-prose"], [class~="not-prose"] *)) { &:not(:first-child) { - padding-top: 28px; + padding-top: var(--heading-4-padding-top); } - padding-bottom: 4px; + padding-bottom: var(--heading-4-padding-bottom); font-size: var(--font-size-h4); line-height: var(--line-height-h4); font-weight: 600; @@ -379,10 +379,10 @@ p.editor-paragraph-block { .prose :where(h5.editor-heading-block):not(:where([class~="not-prose"], [class~="not-prose"] *)) { &:not(:first-child) { - padding-top: 20px; + padding-top: var(--heading-5-padding-top); } - padding-bottom: 4px; + padding-bottom: var(--heading-5-padding-bottom); font-size: var(--font-size-h5); line-height: var(--line-height-h5); font-weight: 600; @@ -390,10 +390,10 @@ p.editor-paragraph-block { .prose :where(h6.editor-heading-block):not(:where([class~="not-prose"], [class~="not-prose"] *)) { &:not(:first-child) { - padding-top: 20px; + padding-top: var(--heading-6-padding-top); } - padding-bottom: 4px; + padding-bottom: var(--heading-6-padding-bottom); font-size: var(--font-size-h6); line-height: var(--line-height-h6); font-weight: 600; @@ -405,16 +405,16 @@ p.editor-paragraph-block { } &:not(:first-child) { - padding-top: 4px; + padding-top: var(--paragraph-padding-top); } &:not(td p.editor-paragraph-block, th p.editor-paragraph-block) { &:last-child { - padding-bottom: 4px; + padding-bottom: var(--paragraph-padding-bottom); } &:not(:last-child) { - padding-bottom: 8px; + padding-bottom: var(--paragraph-padding-between); } } @@ -423,7 +423,7 @@ p.editor-paragraph-block { } p.editor-paragraph-block + p.editor-paragraph-block { - padding-top: 8px !important; + padding-top: var(--paragraph-padding-between) !important; } .prose :where(ol):not(:where([class~="not-prose"], [class~="not-prose"] *)) li p.editor-paragraph-block, diff --git a/packages/editor/src/styles/variables.css b/packages/editor/src/styles/variables.css index 66880f156..ea70fe1ab 100644 --- a/packages/editor/src/styles/variables.css +++ b/packages/editor/src/styles/variables.css @@ -57,4 +57,48 @@ --font-style: monospace; } /* end font styles */ + + /* spacing */ + &.line-spacing-regular { + --heading-1-padding-top: 28px; + --heading-1-padding-bottom: 4px; + --heading-2-padding-top: 28px; + --heading-2-padding-bottom: 4px; + --heading-3-padding-top: 28px; + --heading-3-padding-bottom: 4px; + --heading-4-padding-top: 28px; + --heading-4-padding-bottom: 4px; + --heading-5-padding-top: 20px; + --heading-5-padding-bottom: 4px; + --heading-6-padding-top: 20px; + --heading-6-padding-bottom: 4px; + --paragraph-padding-top: 4px; + --paragraph-padding-bottom: 4px; + --paragraph-padding-between: 8px; + --list-spacing-y: 8px; + --divider-padding-top: 4px; + --divider-padding-bottom: 4px; + } + + &.line-spacing-small { + --heading-1-padding-top: 16px; + --heading-1-padding-bottom: 4px; + --heading-2-padding-top: 16px; + --heading-2-padding-bottom: 4px; + --heading-3-padding-top: 16px; + --heading-3-padding-bottom: 4px; + --heading-4-padding-top: 16px; + --heading-4-padding-bottom: 4px; + --heading-5-padding-top: 12px; + --heading-5-padding-bottom: 4px; + --heading-6-padding-top: 12px; + --heading-6-padding-bottom: 4px; + --paragraph-padding-top: 2px; + --paragraph-padding-bottom: 2px; + --paragraph-padding-between: 4px; + --list-spacing-y: 0px; + --divider-padding-top: 0px; + --divider-padding-bottom: 4px; + } + /* end spacing */ } diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 36c38c03c..60c461274 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -1,7 +1,8 @@ { "name": "@plane/eslint-config", "private": true, - "version": "0.25.0", + "version": "0.25.1", + "license": "AGPL-3.0", "files": [ "library.js", "next.js", diff --git a/packages/hooks/package.json b/packages/hooks/package.json index add45cb94..45cd1c88b 100644 --- a/packages/hooks/package.json +++ b/packages/hooks/package.json @@ -1,6 +1,7 @@ { "name": "@plane/hooks", - "version": "0.25.0", + "version": "0.25.1", + "license": "AGPL-3.0", "description": "React hooks that are shared across multiple apps internally", "private": true, "main": "./dist/index.js", diff --git a/packages/i18n/package.json b/packages/i18n/package.json index b4e4f3999..90063b8e6 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,7 @@ { "name": "@plane/i18n", - "version": "0.25.0", + "version": "0.25.1", + "license": "AGPL-3.0", "description": "I18n shared across multiple apps internally", "private": true, "main": "./src/index.ts", diff --git a/packages/i18n/src/constants/language.ts b/packages/i18n/src/constants/language.ts index 67b8e9d3f..58c31f4b4 100644 --- a/packages/i18n/src/constants/language.ts +++ b/packages/i18n/src/constants/language.ts @@ -8,6 +8,8 @@ export const SUPPORTED_LANGUAGES: ILanguageOption[] = [ { label: "Español", value: "es" }, { label: "日本語", value: "ja" }, { label: "中文", value: "zh-CN" }, + { label: "Русский", value: "ru" }, + { label: "Italian", value: "it" }, ]; export const STORAGE_KEY = "userLanguage"; diff --git a/packages/i18n/src/locales/en/translations.json b/packages/i18n/src/locales/en/translations.json index 7c74ff2ee..f61278c60 100644 --- a/packages/i18n/src/locales/en/translations.json +++ b/packages/i18n/src/locales/en/translations.json @@ -461,6 +461,8 @@ "states": "States", "state": "State", "state_groups": "State groups", + "state_group": "State group", + "priorities": "Priorities", "priority": "Priority", "team_project": "Team project", "project": "Project", @@ -469,12 +471,16 @@ "module": "Module", "modules": "Modules", "labels": "Labels", + "label": "Label", "assignees": "Assignees", "assignee": "Assignee", "created_by": "Created by", "none": "None", "link": "Link", + "estimates": "Estimates", "estimate": "Estimate", + "created_at": "Created at", + "completed_at": "Completed at", "layout": "Layout", "filters": "Filters", "display": "Display", @@ -517,7 +523,6 @@ "add_more": "Add more", "defaults": "Defaults", "add_label": "Add label", - "estimates": "Estimates", "customize_time_range": "Customize time range", "loading": "Loading", "attachments": "Attachments", @@ -656,8 +661,6 @@ "select": "Select", "upgrade": "Upgrade", "add_seats": "Add Seats", - "label": "Label", - "priorities": "Priorities", "projects": "Projects", "workspace": "Workspace", "workspaces": "Workspaces", diff --git a/packages/i18n/src/locales/es/translations.json b/packages/i18n/src/locales/es/translations.json index bc57dba1d..12ba162f2 100644 --- a/packages/i18n/src/locales/es/translations.json +++ b/packages/i18n/src/locales/es/translations.json @@ -633,6 +633,8 @@ "states": "Estados", "state": "Estado", "state_groups": "Grupos de estados", + "state_group": "Grupos de estado", + "priorities": "Prioridades", "priority": "Prioridad", "team_project": "Proyecto de equipo", "project": "Proyecto", @@ -641,12 +643,16 @@ "module": "Módulo", "modules": "Módulos", "labels": "Etiquetas", + "label": "Etiqueta", "assignees": "Asignados", "assignee": "Asignado", "created_by": "Creado por", "none": "Ninguno", "link": "Enlace", + "estimates": "Estimaciones", "estimate": "Estimación", + "created_at": "Creado en", + "completed_at": "Completado en", "layout": "Diseño", "filters": "Filtros", "display": "Mostrar", @@ -689,7 +695,6 @@ "add_more": "Agregar más", "defaults": "Valores predeterminados", "add_label": "Agregar etiqueta", - "estimates": "Estimaciones", "customize_time_range": "Personalizar rango de tiempo", "loading": "Cargando", "attachments": "Archivos adjuntos", @@ -827,8 +832,6 @@ "select": "Seleccionar", "upgrade": "Mejorar", "add_seats": "Agregar asientos", - "label": "Etiqueta", - "priorities": "Prioridades", "projects": "Proyectos", "workspace": "Espacio de trabajo", "workspaces": "Espacios de trabajo", diff --git a/packages/i18n/src/locales/fr/translations.json b/packages/i18n/src/locales/fr/translations.json index f258d5381..855265f48 100644 --- a/packages/i18n/src/locales/fr/translations.json +++ b/packages/i18n/src/locales/fr/translations.json @@ -631,6 +631,8 @@ "states": "États", "state": "État", "state_groups": "Groupes d'états", + "state_group": "Groupe d'état", + "priorities": "Priorités", "priority": "Priorité", "team_project": "Projet d'équipe", "project": "Projet", @@ -639,12 +641,16 @@ "module": "Module", "modules": "Modules", "labels": "Étiquettes", + "label": "Étiquette", "assignees": "Assignés", "assignee": "Assigné", "created_by": "Créé par", "none": "Aucun", "link": "Lien", + "estimates": "Estimations", "estimate": "Estimation", + "created_at": "Créé le", + "completed_at": "Terminé le", "layout": "Disposition", "filters": "Filtres", "display": "Affichage", @@ -687,7 +693,6 @@ "add_more": "Ajouter plus", "defaults": "Par défaut", "add_label": "Ajouter une étiquette", - "estimates": "Estimations", "customize_time_range": "Personnaliser la plage de temps", "loading": "Chargement", "attachments": "Pièces jointes", @@ -825,8 +830,6 @@ "select": "Sélectionner", "upgrade": "Mettre à niveau", "add_seats": "Ajouter des sièges", - "label": "Étiquette", - "priorities": "Priorités", "projects": "Projets", "workspace": "Espace de travail", "workspaces": "Espaces de travail", diff --git a/packages/i18n/src/locales/it/translations.json b/packages/i18n/src/locales/it/translations.json new file mode 100644 index 000000000..2bd43107f --- /dev/null +++ b/packages/i18n/src/locales/it/translations.json @@ -0,0 +1,2363 @@ +{ + "sidebar": { + "projects": "Progetti", + "pages": "Pagine", + "new_work_item": "Nuovo elemento di lavoro", + "home": "Home", + "your_work": "Il tuo lavoro", + "inbox": "Posta in arrivo", + "workspace": "workspace", + "views": "Visualizzazioni", + "analytics": "Analisi", + "work_items": "Elementi di lavoro", + "cycles": "Cicli", + "modules": "Moduli", + "intake": "Intake", + "drafts": "Bozze", + "favorites": "Preferiti", + "pro": "Pro", + "upgrade": "Aggiorna" + }, + + "auth": { + "common": { + "email": { + "label": "Email", + "placeholder": "nome@azienda.com", + "errors": { + "required": "Email è obbligatoria", + "invalid": "Email non valida" + } + }, + "password": { + "label": "Password", + "set_password": "Imposta una password", + "placeholder": "Inserisci la password", + "confirm_password": { + "label": "Conferma password", + "placeholder": "Conferma password" + }, + "current_password": { + "label": "Password attuale" + }, + "new_password": { + "label": "Nuova password", + "placeholder": "Inserisci nuova password" + }, + "change_password": { + "label": { + "default": "Cambia password", + "submitting": "Cambiando password" + } + }, + "errors": { + "match": "Le password non corrispondono", + "empty": "Per favore inserisci la tua password", + "length": "La lunghezza della password deve essere superiore a 8 caratteri", + "strength": { + "weak": "La password è debole", + "strong": "La password è forte" + } + }, + "submit": "Imposta password", + "toast": { + "change_password": { + "success": { + "title": "Successo!", + "message": "Password cambiata con successo." + }, + "error": { + "title": "Errore!", + "message": "Qualcosa è andato storto. Per favore riprova." + } + } + } + }, + "unique_code": { + "label": "Codice unico", + "placeholder": "gets-sets-flys", + "paste_code": "Incolla il codice inviato alla tua email", + "requesting_new_code": "Richiesta di nuovo codice", + "sending_code": "Invio codice" + }, + "already_have_an_account": "Hai già un account?", + "login": "Accedi", + "create_account": "Crea un account", + "new_to_plane": "Nuovo su Plane?", + "back_to_sign_in": "Torna al login", + "resend_in": "Reinvia in {seconds} secondi", + "sign_in_with_unique_code": "Accedi con codice unico", + "forgot_password": "Hai dimenticato la password?" + }, + "sign_up": { + "header": { + "label": "Crea un account per iniziare a gestire il lavoro con il tuo team.", + "step": { + "email": { + "header": "Registrati", + "sub_header": "" + }, + "password": { + "header": "Registrati", + "sub_header": "Registrati utilizzando una combinazione email-password." + }, + "unique_code": { + "header": "Registrati", + "sub_header": "Registrati utilizzando un codice unico inviato all'indirizzo email sopra." + } + } + }, + "errors": { + "password": { + "strength": "Prova a impostare una password forte per procedere" + } + } + }, + "sign_in": { + "header": { + "label": "Accedi per iniziare a gestire il lavoro con il tuo team.", + "step": { + "email": { + "header": "Accedi o registrati", + "sub_header": "" + }, + "password": { + "header": "Accedi o registrati", + "sub_header": "Usa la tua combinazione email-password per accedere." + }, + "unique_code": { + "header": "Accedi o registrati", + "sub_header": "Accedi utilizzando un codice unico inviato all'indirizzo email sopra." + } + } + } + }, + "forgot_password": { + "title": "Reimposta la tua password", + "description": "Inserisci l'indirizzo email verificato del tuo account utente e ti invieremo un link per reimpostare la password.", + "email_sent": "Abbiamo inviato il link di reimpostazione al tuo indirizzo email", + "send_reset_link": "Invia link di reimpostazione", + "errors": { + "smtp_not_enabled": "Vediamo che il tuo amministratore non ha abilitato SMTP, non saremo in grado di inviare un link di reimpostazione della password" + }, + "toast": { + "success": { + "title": "Email inviata", + "message": "Controlla la tua inbox per un link per reimpostare la tua password. Se non appare entro pochi minuti, controlla la tua cartella spam." + }, + "error": { + "title": "Errore!", + "message": "Qualcosa è andato storto. Per favore riprova." + } + } + }, + "reset_password": { + "title": "Imposta nuova password", + "description": "Proteggi il tuo account con una password forte" + }, + "set_password": { + "title": "Proteggi il tuo account", + "description": "Impostare una password ti aiuta a accedere in modo sicuro" + }, + "sign_out": { + "toast": { + "error": { + "title": "Errore!", + "message": "Impossibile disconnettersi. Per favore riprova." + } + } + } + }, + "submit": "Conferma", + "cancel": "Annulla", + "loading": "Caricamento", + "error": "Errore", + "success": "Successo", + "warning": "Avviso", + "info": "Informazioni", + "close": "Chiudi", + "yes": "Sì", + "no": "No", + "ok": "OK", + "name": "Nome", + "description": "Descrizione", + "search": "Cerca", + "add_member": "Aggiungi membro", + "adding_members": "Aggiungendo membri", + "remove_member": "Rimuovi membro", + "add_members": "Aggiungi membri", + "adding_member": "Aggiungendo membro", + "remove_members": "Rimuovi membri", + "add": "Aggiungi", + "adding": "Aggiungendo", + "remove": "Rimuovi", + "add_new": "Aggiungi nuovo", + "remove_selected": "Rimuovi selezionati", + "first_name": "Nome", + "last_name": "Cognome", + "email": "Email", + "display_name": "Nome visualizzato", + "role": "Ruolo", + "timezone": "Fuso orario", + "avatar": "Avatar", + "cover_image": "Immagine di copertina", + "password": "Password", + "change_cover": "Cambia copertina", + "language": "Lingua", + "saving": "Salvataggio in corso", + "save_changes": "Salva modifiche", + "deactivate_account": "Disattiva account", + "deactivate_account_description": "Disattivando un account, tutti i dati e le risorse al suo interno verranno rimossi definitivamente e non potranno essere recuperati.", + "profile_settings": "Impostazioni del profilo", + "your_account": "Il tuo account", + "security": "Sicurezza", + "activity": "Attività", + "appearance": "Aspetto", + "notifications": "Notifiche", + "workspaces": "Spazi di lavoro", + "create_workspace": "Crea spazio di lavoro", + "invitations": "Inviti", + "summary": "Riepilogo", + "assigned": "Assegnato", + "created": "Creato", + "subscribed": "Iscritto", + "you_do_not_have_the_permission_to_access_this_page": "Non hai il permesso di accedere a questa pagina.", + "something_went_wrong_please_try_again": "Qualcosa è andato storto. Per favore, riprova.", + "load_more": "Carica di più", + "select_or_customize_your_interface_color_scheme": "Seleziona o personalizza lo schema dei colori dell'interfaccia.", + "theme": "Tema", + "system_preference": "Preferenza di sistema", + "light": "Chiaro", + "dark": "Scuro", + "light_contrast": "Contrasto elevato chiaro", + "dark_contrast": "Contrasto elevato scuro", + "custom": "Tema personalizzato", + "select_your_theme": "Seleziona il tuo tema", + "customize_your_theme": "Personalizza il tuo tema", + "background_color": "Colore di sfondo", + "text_color": "Colore del testo", + "primary_color": "Colore primario (Tema)", + "sidebar_background_color": "Colore di sfondo della barra laterale", + "sidebar_text_color": "Colore del testo della barra laterale", + "set_theme": "Imposta tema", + "enter_a_valid_hex_code_of_6_characters": "Inserisci un codice esadecimale valido di 6 caratteri", + "background_color_is_required": "Il colore di sfondo è obbligatorio", + "text_color_is_required": "Il colore del testo è obbligatorio", + "primary_color_is_required": "Il colore primario è obbligatorio", + "sidebar_background_color_is_required": "Il colore di sfondo della barra laterale è obbligatorio", + "sidebar_text_color_is_required": "Il colore del testo della barra laterale è obbligatorio", + "updating_theme": "Aggiornamento del tema in corso", + "theme_updated_successfully": "Tema aggiornato con successo", + "failed_to_update_the_theme": "Impossibile aggiornare il tema", + "email_notifications": "Notifiche via email", + "stay_in_the_loop_on_issues_you_are_subscribed_to_enable_this_to_get_notified": "Rimani aggiornato sugli elementi di lavoro a cui sei iscritto. Abilita questa opzione per ricevere notifiche.", + "email_notification_setting_updated_successfully": "Impostazioni delle notifiche email aggiornate con successo", + "failed_to_update_email_notification_setting": "Impossibile aggiornare le impostazioni delle notifiche email", + "notify_me_when": "Avvisami quando", + "property_changes": "Modifiche alle proprietà", + "property_changes_description": "Avvisami quando le proprietà degli elementi di lavoro, come assegnatari, priorità, stime o altro, cambiano.", + "state_change": "Cambio di stato", + "state_change_description": "Avvisami quando l'elemento di lavoro passa a uno stato diverso", + "issue_completed": "Elemento di lavoro completato", + "issue_completed_description": "Avvisami solo quando un elemento di lavoro è completato", + "comments": "Commenti", + "comments_description": "Avvisami quando qualcuno lascia un commento sull'elemento di lavoro", + "mentions": "Menzioni", + "mentions_description": "Avvisami solo quando qualcuno mi menziona nei commenti o nella descrizione", + "old_password": "Vecchia password", + "general_settings": "Impostazioni generali", + "sign_out": "Esci", + "signing_out": "Uscita in corso", + "active_cycles": "Cicli attivi", + "active_cycles_description": "Monitora i cicli attraverso i progetti, segui gli elementi di lavoro ad alta priorità e analizza i cicli che necessitano attenzione.", + "on_demand_snapshots_of_all_your_cycles": "Snapshot on-demand di tutti i tuoi cicli", + "upgrade": "Aggiorna", + "10000_feet_view": "Vista panoramica (10.000 piedi) di tutti i cicli attivi.", + "10000_feet_view_description": "Effettua uno zoom indietro per vedere i cicli in esecuzione in tutti i tuoi progetti contemporaneamente, invece di passare da un ciclo all'altro in ogni progetto.", + "get_snapshot_of_each_active_cycle": "Ottieni uno snapshot di ogni ciclo attivo.", + "get_snapshot_of_each_active_cycle_description": "Monitora metriche di alto livello per tutti i cicli attivi, osserva il loro stato di avanzamento e valuta la portata rispetto alle scadenze.", + "compare_burndowns": "Confronta i burndown.", + "compare_burndowns_description": "Monitora le prestazioni di ciascun team con una rapida occhiata al report del burndown di ogni ciclo.", + "quickly_see_make_or_break_issues": "Visualizza rapidamente gli elementi di lavoro critici.", + "quickly_see_make_or_break_issues_description": "Visualizza in anteprima gli elementi di lavoro ad alta priorità per ogni ciclo in base alle scadenze. Vedi tutti con un solo clic.", + "zoom_into_cycles_that_need_attention": "Zoom sui cicli che richiedono attenzione.", + "zoom_into_cycles_that_need_attention_description": "Esamina lo stato di ogni ciclo che non rispetta le aspettative con un clic.", + "stay_ahead_of_blockers": "Anticipa gli ostacoli.", + "stay_ahead_of_blockers_description": "Individua le sfide tra i progetti e visualizza le dipendenze inter-cicliche non evidenti in altre viste.", + "analytics": "Analisi", + "workspace_invites": "Inviti allo spazio di lavoro", + "enter_god_mode": "Entra in modalità dio", + "workspace_logo": "Logo dello spazio di lavoro", + "new_issue": "Nuovo elemento di lavoro", + "your_work": "Il tuo lavoro", + "drafts": "Bozze", + "projects": "Progetti", + "views": "Visualizzazioni", + "workspace": "Spazio di lavoro", + "archives": "Archivi", + "settings": "Impostazioni", + "failed_to_move_favorite": "Impossibile spostare il preferito", + "favorites": "Preferiti", + "no_favorites_yet": "Nessun preferito ancora", + "create_folder": "Crea cartella", + "new_folder": "Nuova cartella", + "favorite_updated_successfully": "Preferito aggiornato con successo", + "favorite_created_successfully": "Preferito creato con successo", + "folder_already_exists": "La cartella esiste già", + "folder_name_cannot_be_empty": "Il nome della cartella non può essere vuoto", + "something_went_wrong": "Qualcosa è andato storto", + "failed_to_reorder_favorite": "Impossibile riordinare il preferito", + "favorite_removed_successfully": "Preferito rimosso con successo", + "failed_to_create_favorite": "Impossibile creare il preferito", + "failed_to_rename_favorite": "Impossibile rinominare il preferito", + "project_link_copied_to_clipboard": "Link del progetto copiato negli appunti", + "link_copied": "Link copiato", + "add_project": "Aggiungi progetto", + "create_project": "Crea progetto", + "failed_to_remove_project_from_favorites": "Impossibile rimuovere il progetto dai preferiti. Per favore, riprova.", + "project_created_successfully": "Progetto creato con successo", + "project_created_successfully_description": "Progetto creato con successo. Ora puoi iniziare ad aggiungere elementi di lavoro.", + "project_cover_image_alt": "Immagine di copertina del progetto", + "name_is_required": "Il nome è obbligatorio", + "title_should_be_less_than_255_characters": "Il titolo deve contenere meno di 255 caratteri", + "project_name": "Nome del progetto", + "project_id_must_be_at_least_1_character": "L'ID del progetto deve contenere almeno 1 carattere", + "project_id_must_be_at_most_5_characters": "L'ID del progetto deve contenere al massimo 5 caratteri", + "project_id": "ID del progetto", + "project_id_tooltip_content": "Ti aiuta a identificare in modo univoco gli elementi di lavoro nel progetto. Massimo 5 caratteri.", + "description_placeholder": "Descrizione", + "only_alphanumeric_non_latin_characters_allowed": "Sono ammessi solo caratteri alfanumerici e non latini.", + "project_id_is_required": "L'ID del progetto è obbligatorio", + "project_id_allowed_char": "Sono ammessi solo caratteri alfanumerici e non latini.", + "project_id_min_char": "L'ID del progetto deve contenere almeno 1 carattere", + "project_id_max_char": "L'ID del progetto deve contenere al massimo 5 caratteri", + "project_description_placeholder": "Inserisci la descrizione del progetto", + "select_network": "Seleziona rete", + "lead": "Responsabile", + "date_range": "Intervallo di date", + "private": "Privato", + "public": "Pubblico", + "accessible_only_by_invite": "Accessibile solo su invito", + "anyone_in_the_workspace_except_guests_can_join": "Chiunque nello spazio di lavoro, tranne gli ospiti, può unirsi", + "creating": "Creazione in corso", + "creating_project": "Creazione del progetto in corso", + "adding_project_to_favorites": "Aggiunta del progetto ai preferiti in corso", + "project_added_to_favorites": "Progetto aggiunto ai preferiti", + "couldnt_add_the_project_to_favorites": "Impossibile aggiungere il progetto ai preferiti. Per favore, riprova.", + "removing_project_from_favorites": "Rimozione del progetto dai preferiti in corso", + "project_removed_from_favorites": "Progetto rimosso dai preferiti", + "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_settings": "Impostazioni di pubblicazione", + "publish": "Pubblica", + "copy_link": "Copia link", + "leave_project": "Lascia progetto", + "join_the_project_to_rearrange": "Unisciti al progetto per riorganizzare", + "drag_to_rearrange": "Trascina per riorganizzare", + "congrats": "Congratulazioni!", + "open_project": "Apri progetto", + "issues": "Elementi di lavoro", + "cycles": "Cicli", + "modules": "Moduli", + "pages": "Pagine", + "intake": "Accoglienza", + "time_tracking": "Tracciamento del tempo", + "work_management": "Gestione del lavoro", + "projects_and_issues": "Progetti ed elementi di lavoro", + "projects_and_issues_description": "Attiva o disattiva queste opzioni per questo progetto.", + "cycles_description": "Definisci i cicli di lavoro per progetto e modifica la frequenza da un periodo all'altro.", + "modules_description": "Raggruppa il lavoro in configurazioni simili a sotto-progetti con i propri responsabili e assegnatari.", + "views_description": "Salva ordinamenti, filtri e opzioni di visualizzazione per dopo o condividili.", + "pages_description": "Scrivi qualsiasi cosa, come faresti normalmente.", + "intake_description": "Rimani aggiornato sugli elementi di lavoro a cui sei iscritto. Abilita questa opzione per ricevere notifiche.", + "time_tracking_description": "Traccia il tempo speso sugli elementi di lavoro e sui progetti.", + "work_management_description": "Gestisci il tuo lavoro e i tuoi progetti con facilità.", + "documentation": "Documentazione", + "message_support": "Contatta il supporto", + "contact_sales": "Contatta le vendite", + "hyper_mode": "Modalità Hyper", + "keyboard_shortcuts": "Scorciatoie da tastiera", + "whats_new": "Novità?", + "version": "Versione", + "we_are_having_trouble_fetching_the_updates": "Stiamo riscontrando problemi nel recuperare gli aggiornamenti.", + "our_changelogs": "i nostri changelog", + "for_the_latest_updates": "per gli ultimi aggiornamenti.", + "please_visit": "Per favore visita", + "docs": "Documentazione", + "full_changelog": "Changelog completo", + "support": "Supporto", + "discord": "Discord", + "powered_by_plane_pages": "Supportato da Plane Pages", + "please_select_at_least_one_invitation": "Seleziona almeno un invito.", + "please_select_at_least_one_invitation_description": "Seleziona almeno un invito per unirti allo spazio di lavoro.", + "we_see_that_someone_has_invited_you_to_join_a_workspace": "Abbiamo notato che qualcuno ti ha invitato a unirti a uno spazio di lavoro", + "join_a_workspace": "Unisciti a uno spazio di lavoro", + "we_see_that_someone_has_invited_you_to_join_a_workspace_description": "Abbiamo notato che qualcuno ti ha invitato a unirti a uno spazio di lavoro", + "join_a_workspace_description": "Unisciti a uno spazio di lavoro", + "accept_and_join": "Accetta e unisciti", + "go_home": "Vai alla home", + "no_pending_invites": "Nessun invito in sospeso", + "you_can_see_here_if_someone_invites_you_to_a_workspace": "Qui puoi vedere se qualcuno ti invita a uno spazio di lavoro", + "back_to_home": "Torna alla home", + "workspace_name": "nome-spazio-di-lavoro", + "deactivate_your_account": "Disattiva il tuo account", + "deactivate_your_account_description": "Una volta disattivato, non potrai più essere assegnato a elementi di lavoro né addebitato per il tuo spazio di lavoro. Per riattivare il tuo account, avrai bisogno di un invito a uno spazio di lavoro associato a questo indirizzo email.", + "deactivating": "Disattivazione in corso", + "confirm": "Conferma", + "confirming": "Conferma in corso", + "draft_created": "Bozza creata", + "issue_created_successfully": "Elemento di lavoro creato con successo", + "draft_creation_failed": "Creazione della bozza fallita", + "issue_creation_failed": "Creazione dell'elemento di lavoro fallita", + "draft_issue": "Bozza di elemento di lavoro", + "issue_updated_successfully": "Elemento di lavoro aggiornato con successo", + "issue_could_not_be_updated": "Impossibile aggiornare l'elemento di lavoro", + "create_a_draft": "Crea una bozza", + "save_to_drafts": "Salva nelle bozze", + "save": "Salva", + "update": "Aggiorna", + "updating": "Aggiornamento in corso", + "create_new_issue": "Crea un nuovo elemento di lavoro", + "editor_is_not_ready_to_discard_changes": "L'editor non è pronto per scartare le modifiche", + "failed_to_move_issue_to_project": "Impossibile spostare l'elemento di lavoro nel progetto", + "create_more": "Crea altri", + "add_to_project": "Aggiungi al progetto", + "discard": "Scarta", + "duplicate_issue_found": "Elemento di lavoro duplicato trovato", + "duplicate_issues_found": "Elementi di lavoro duplicati trovati", + "no_matching_results": "Nessun risultato corrispondente", + "title_is_required": "Il titolo è obbligatorio", + "title": "Titolo", + "state": "Stato", + "priority": "Priorità", + "none": "Nessuna", + "urgent": "Urgente", + "high": "Alta", + "medium": "Media", + "low": "Bassa", + "members": "Membri", + "assignee": "Assegnatario", + "assignees": "Assegnatari", + "you": "Tu", + "labels": "Etichette", + "create_new_label": "Crea nuova etichetta", + "start_date": "Data di inizio", + "end_date": "Data di fine", + "due_date": "Scadenza", + "estimate": "Stima", + "change_parent_issue": "Cambia elemento di lavoro principale", + "remove_parent_issue": "Rimuovi elemento di lavoro principale", + "add_parent": "Aggiungi elemento principale", + "loading_members": "Caricamento membri", + "view_link_copied_to_clipboard": "Link di visualizzazione copiato negli appunti.", + "required": "Obbligatorio", + "optional": "Opzionale", + "Cancel": "Annulla", + "edit": "Modifica", + "archive": "Archivia", + "restore": "Ripristina", + "open_in_new_tab": "Apri in una nuova scheda", + "delete": "Elimina", + "deleting": "Eliminazione in corso", + "make_a_copy": "Crea una copia", + "move_to_project": "Sposta nel progetto", + "good": "Buono", + "morning": "Mattina", + "afternoon": "Pomeriggio", + "evening": "Sera", + "show_all": "Mostra tutto", + "show_less": "Mostra meno", + "no_data_yet": "Nessun dato disponibile", + "syncing": "Sincronizzazione in corso", + "add_work_item": "Aggiungi elemento di lavoro", + "advanced_description_placeholder": "Premi '/' per i comandi", + "create_work_item": "Crea elemento di lavoro", + "attachments": "Allegati", + "declining": "Rifiuto in corso", + "declined": "Rifiutato", + "decline": "Rifiuta", + "unassigned": "Non assegnato", + "work_items": "Elementi di lavoro", + "add_link": "Aggiungi link", + "points": "Punti", + "no_assignee": "Nessun assegnatario", + "no_assignees_yet": "Nessun assegnatario ancora", + "no_labels_yet": "Nessuna etichetta ancora", + "ideal": "Ideale", + "current": "Corrente", + "no_matching_members": "Nessun membro corrispondente", + "leaving": "Uscita in corso", + "removing": "Rimozione in corso", + "leave": "Esci", + "refresh": "Aggiorna", + "refreshing": "Aggiornamento in corso", + "refresh_status": "Stato dell'aggiornamento", + "prev": "Precedente", + "next": "Successivo", + "re_generating": "Rigenerazione in corso", + "re_generate": "Rigenera", + "re_generate_key": "Rigenera chiave", + "export": "Esporta", + "member": "{count, plural, one {# membro} other {# membri}}", + + "project_view": { + "sort_by": { + "created_at": "Creato il", + "updated_at": "Aggiornato il", + "name": "Nome" + } + }, + + "toast": { + "success": "Successo!", + "error": "Errore!" + }, + + "links": { + "toasts": { + "created": { + "title": "Link creato", + "message": "Il link è stato creato con successo" + }, + "not_created": { + "title": "Link non creato", + "message": "Il link non può essere creato" + }, + "updated": { + "title": "Link aggiornato", + "message": "Il link è stato aggiornato con successo" + }, + "not_updated": { + "title": "Link non aggiornato", + "message": "Il link non può essere aggiornato" + }, + "removed": { + "title": "Link rimosso", + "message": "Il link è stato rimosso con successo" + }, + "not_removed": { + "title": "Link non rimosso", + "message": "Il link non può essere rimosso" + } + } + }, + + "home": { + "empty": { + "quickstart_guide": "La tua guida rapida", + "not_right_now": "Non ora", + "create_project": { + "title": "Crea un progetto", + "description": "La maggior parte delle cose inizia con un progetto in Plane.", + "cta": "Inizia" + }, + "invite_team": { + "title": "Invita il tuo team", + "description": "Collabora, lancia e gestisci insieme ai colleghi.", + "cta": "Invitali" + }, + "configure_workspace": { + "title": "Configura il tuo spazio di lavoro.", + "description": "Attiva o disattiva le funzionalità o personalizza ulteriormente.", + "cta": "Configura questo spazio" + }, + "personalize_account": { + "title": "Rendi Plane tuo.", + "description": "Scegli la tua immagine, i colori e altro.", + "cta": "Personalizza ora" + }, + "widgets": { + "title": "È silenzioso senza widget, attivali", + "description": "Sembra che tutti i tuoi widget siano disattivati. Attivali ora per migliorare la tua esperienza!", + "primary_button": { + "text": "Gestisci widget" + } + } + }, + "quick_links": { + "empty": "Salva link a elementi di lavoro che ti servono.", + "add": "Aggiungi link rapido", + "title": "Link rapido", + "title_plural": "Link rapidi" + }, + "recents": { + "title": "Recenti", + "empty": { + "project": "I tuoi progetti recenti appariranno qui una volta visitati.", + "page": "Le tue pagine recenti appariranno qui una volta visitate.", + "issue": "I tuoi elementi di lavoro recenti appariranno qui una volta visitati.", + "default": "Non hai ancora elementi recenti." + }, + "filters": { + "all": "Tutti gli elementi", + "projects": "Progetti", + "pages": "Pagine", + "issues": "Elementi di lavoro" + } + }, + "new_at_plane": { + "title": "Novità su Plane" + }, + "quick_tutorial": { + "title": "Tutorial rapido" + }, + "widget": { + "reordered_successfully": "Widget riordinato con successo.", + "reordering_failed": "Si è verificato un errore durante il riordino del widget." + }, + "manage_widgets": "Gestisci widget", + "title": "Home", + "star_us_on_github": "Metti una stella su GitHub" + }, + + "link": { + "modal": { + "url": { + "text": "URL", + "required": "L'URL non è valido", + "placeholder": "Digita o incolla un URL" + }, + "title": { + "text": "Titolo di visualizzazione", + "placeholder": "Come vorresti che apparisse questo link" + } + } + }, + + "common": { + "all": "Tutti", + "states": "Stati", + "state": "Stato", + "state_groups": "Gruppi di stati", + "priority": "Priorità", + "team_project": "Progetto di squadra", + "project": "Progetto", + "cycle": "Ciclo", + "cycles": "Cicli", + "module": "Modulo", + "modules": "Moduli", + "labels": "Etichette", + "assignees": "Assegnatari", + "assignee": "Assegnatario", + "created_by": "Creato da", + "none": "Nessuno", + "link": "Link", + "estimate": "Stima", + "layout": "Layout", + "filters": "Filtri", + "display": "Visualizza", + "load_more": "Carica di più", + "activity": "Attività", + "analytics": "Analisi", + "dates": "Date", + "success": "Successo!", + "something_went_wrong": "Qualcosa è andato storto", + "error": { + "label": "Errore!", + "message": "Si è verificato un errore. Per favore, riprova." + }, + "group_by": "Raggruppa per", + "epic": "Epic", + "epics": "Epic", + "work_item": "Elemento di lavoro", + "work_items": "Elementi di lavoro", + "sub_work_item": "Sotto-elemento di lavoro", + "add": "Aggiungi", + "warning": "Avviso", + "updating": "Aggiornamento in corso", + "adding": "Aggiunta in corso", + "update": "Aggiorna", + "creating": "Creazione in corso", + "create": "Crea", + "cancel": "Annulla", + "description": "Descrizione", + "title": "Titolo", + "attachment": "Allegato", + "general": "Generale", + "features": "Funzionalità", + "automation": "Automazione", + "project_name": "Nome del progetto", + "project_id": "ID del progetto", + "project_timezone": "Fuso orario del progetto", + "created_on": "Creato il", + "update_project": "Aggiorna progetto", + "identifier_already_exists": "L'identificatore esiste già", + "add_more": "Aggiungi altro", + "defaults": "Predefiniti", + "add_label": "Aggiungi etichetta", + "estimates": "Stime", + "customize_time_range": "Personalizza intervallo di tempo", + "loading": "Caricamento", + "attachments": "Allegati", + "property": "Proprietà", + "properties": "Proprietà", + "parent": "Principale", + "remove": "Rimuovi", + "archiving": "Archiviazione in corso", + "archive": "Archivia", + "access": { + "public": "Pubblico", + "private": "Privato" + }, + "done": "Fatto", + "sub_work_items": "Sotto-elementi di lavoro", + "comment": "Commento", + "workspace_level": "Livello dello spazio di lavoro", + "order_by": { + "label": "Ordina per", + "manual": "Manuale", + "last_created": "Ultimo creato", + "last_updated": "Ultimo aggiornato", + "start_date": "Data di inizio", + "due_date": "Scadenza", + "asc": "Ascendente", + "desc": "Discendente", + "updated_on": "Aggiornato il" + }, + "sort": { + "asc": "Ascendente", + "desc": "Discendente", + "created_on": "Creato il", + "updated_on": "Aggiornato il" + }, + "comments": "Commenti", + "updates": "Aggiornamenti", + "clear_all": "Pulisci tutto", + "copied": "Copiato!", + "link_copied": "Link copiato!", + "link_copied_to_clipboard": "Link copiato negli appunti", + "copied_to_clipboard": "Link dell'elemento di lavoro copiato negli appunti", + "is_copied_to_clipboard": "Elemento di lavoro copiato negli appunti", + "no_links_added_yet": "Nessun link aggiunto ancora", + "add_link": "Aggiungi link", + "links": "Link", + "go_to_workspace": "Vai allo spazio di lavoro", + "progress": "Progresso", + "optional": "Opzionale", + "join": "Unisciti", + "go_back": "Torna indietro", + "continue": "Continua", + "resend": "Reinvia", + "relations": "Relazioni", + "errors": { + "default": { + "title": "Errore!", + "message": "Qualcosa è andato storto. Per favore, riprova." + }, + "required": "Questo campo è obbligatorio", + "entity_required": "{entity} è obbligatorio" + }, + "update_link": "Aggiorna link", + "attach": "Allega", + "create_new": "Crea nuovo", + "add_existing": "Aggiungi esistente", + "type_or_paste_a_url": "Digita o incolla un URL", + "url_is_invalid": "L'URL non è valido", + "display_title": "Titolo di visualizzazione", + "link_title_placeholder": "Come vorresti vedere questo link", + "url": "URL", + "side_peek": "Visualizzazione laterale", + "modal": "Modal", + "full_screen": "Schermo intero", + "close_peek_view": "Chiudi la visualizzazione rapida", + "toggle_peek_view_layout": "Alterna layout della visualizzazione rapida", + "options": "Opzioni", + "duration": "Durata", + "today": "Oggi", + "week": "Settimana", + "month": "Mese", + "quarter": "Trimestre", + "press_for_commands": "Premi '/' per i comandi", + "click_to_add_description": "Clicca per aggiungere una descrizione", + "search": { + "label": "Cerca", + "placeholder": "Digita per cercare", + "no_matches_found": "Nessuna corrispondenza trovata", + "no_matching_results": "Nessun risultato corrispondente" + }, + "actions": { + "edit": "Modifica", + "make_a_copy": "Crea una copia", + "open_in_new_tab": "Apri in una nuova scheda", + "copy_link": "Copia link", + "archive": "Archivia", + "restore": "Ripristina", + "delete": "Elimina", + "remove_relation": "Rimuovi relazione", + "subscribe": "Iscriviti", + "unsubscribe": "Annulla iscrizione", + "clear_sorting": "Cancella ordinamento", + "show_weekends": "Mostra weekend", + "enable": "Abilita", + "disable": "Disabilita" + }, + "name": "Nome", + "discard": "Scarta", + "confirm": "Conferma", + "confirming": "Conferma in corso", + "read_the_docs": "Leggi la documentazione", + "default": "Predefinito", + "active": "Attivo", + "enabled": "Abilitato", + "disabled": "Disabilitato", + "mandate": "Obbligo", + "mandatory": "Obbligatorio", + "yes": "Sì", + "no": "No", + "please_wait": "Attendere prego", + "enabling": "Abilitazione in corso", + "disabling": "Disabilitazione in corso", + "beta": "Beta", + "or": "o", + "next": "Successivo", + "back": "Indietro", + "cancelling": "Annullamento in corso", + "configuring": "Configurazione in corso", + "clear": "Pulisci", + "import": "Importa", + "connect": "Connetti", + "authorizing": "Autorizzazione in corso", + "processing": "Elaborazione in corso", + "no_data_available": "Nessun dato disponibile", + "from": "da {name}", + "authenticated": "Autenticato", + "select": "Seleziona", + "upgrade": "Aggiorna", + "add_seats": "Aggiungi postazioni", + "label": "Etichetta", + "priorities": "Priorità", + "projects": "Progetti", + "workspace": "Spazio di lavoro", + "workspaces": "Spazi di lavoro", + "team": "Team", + "teams": "Team", + "entity": "Entità", + "entities": "Entità", + "task": "Attività", + "tasks": "Attività", + "section": "Sezione", + "sections": "Sezioni", + "edit": "Modifica", + "connecting": "Connessione in corso", + "connected": "Connesso", + "disconnect": "Disconnetti", + "disconnecting": "Disconnessione in corso", + "installing": "Installazione in corso", + "install": "Installa", + "reset": "Reimposta", + "live": "Live", + "change_history": "Cronologia modifiche", + "coming_soon": "Prossimamente", + "members": "Membri", + "you": "Tu", + "upgrade_cta": { + "higher_subscription": "Passa a un abbonamento superiore", + "talk_to_sales": "Parla con le vendite" + }, + "category": "Categoria", + "categories": "Categorie", + "saving": "Salvataggio in corso", + "save_changes": "Salva modifiche", + "delete": "Elimina", + "deleting": "Eliminazione in corso", + "pending": "In sospeso", + "invite": "Invita" + }, + + "chart": { + "x_axis": "Asse X", + "y_axis": "Asse Y", + "metric": "Metrica" + }, + + "form": { + "title": { + "required": "Il titolo è obbligatorio", + "max_length": "Il titolo deve contenere meno di {length} caratteri" + } + }, + + "entity": { + "grouping_title": "Raggruppamento di {entity}", + "priority": "Priorità di {entity}", + "all": "Tutti {entity}", + "drop_here_to_move": "Trascina qui per spostare il {entity}", + "delete": { + "label": "Elimina {entity}", + "success": "{entity} eliminato con successo", + "failed": "Eliminazione di {entity} fallita" + }, + "update": { + "failed": "Aggiornamento di {entity} fallito", + "success": "{entity} aggiornato con successo" + }, + "link_copied_to_clipboard": "Link di {entity} copiato negli appunti", + "fetch": { + "failed": "Errore durante il recupero di {entity}" + }, + "add": { + "success": "{entity} aggiunto con successo", + "failed": "Errore nell'aggiunta di {entity}" + } + }, + + "epic": { + "all": "Tutti gli Epic", + "label": "{count, plural, one {Epic} other {Epic}}", + "new": "Nuovo Epic", + "adding": "Aggiungendo Epic", + "create": { + "success": "Epic creato con successo" + }, + "add": { + "press_enter": "Premi 'Invio' per aggiungere un altro Epic", + "label": "Aggiungi Epic" + }, + "title": { + "label": "Titolo Epic", + "required": "Il titolo dell'Epic è obbligatorio." + } + }, + + "issue": { + "label": "{count, plural, one {Elemento di lavoro} other {Elementi di lavoro}}", + "all": "Tutti gli elementi di lavoro", + "edit": "Modifica elemento di lavoro", + "title": { + "label": "Titolo dell'elemento di lavoro", + "required": "Il titolo dell'elemento di lavoro è obbligatorio." + }, + "add": { + "press_enter": "Premi 'Invio' per aggiungere un altro elemento di lavoro", + "label": "Aggiungi elemento di lavoro", + "cycle": { + "failed": "Impossibile aggiungere l'elemento di lavoro al ciclo. Per favore, riprova.", + "success": "{count, plural, one {Elemento di lavoro} other {Elementi di lavoro}} aggiunto al ciclo con successo.", + "loading": "Aggiungendo {count, plural, one {elemento di lavoro} other {elementi di lavoro}} al ciclo" + }, + "assignee": "Aggiungi assegnatari", + "start_date": "Aggiungi data di inizio", + "due_date": "Aggiungi scadenza", + "parent": "Aggiungi elemento di lavoro principale", + "sub_issue": "Aggiungi sotto-elemento di lavoro", + "relation": "Aggiungi relazione", + "link": "Aggiungi link", + "existing": "Aggiungi elemento di lavoro esistente" + }, + "remove": { + "label": "Rimuovi elemento di lavoro", + "cycle": { + "loading": "Rimuovendo l'elemento di lavoro dal ciclo", + "success": "Elemento di lavoro rimosso dal ciclo con successo.", + "failed": "Impossibile rimuovere l'elemento di lavoro dal ciclo. Per favore, riprova." + }, + "module": { + "loading": "Rimuovendo l'elemento di lavoro dal modulo", + "success": "Elemento di lavoro rimosso dal modulo con successo.", + "failed": "Impossibile rimuovere l'elemento di lavoro dal modulo. Per favore, riprova." + }, + "parent": { + "label": "Rimuovi elemento di lavoro principale" + } + }, + "new": "Nuovo elemento di lavoro", + "adding": "Aggiunta dell'elemento di lavoro in corso", + "create": { + "success": "Elemento di lavoro creato con successo" + }, + "priority": { + "urgent": "Urgente", + "high": "Alta", + "medium": "Media", + "low": "Bassa" + }, + "display": { + "properties": { + "label": "Visualizza proprietà", + "id": "ID", + "issue_type": "Tipo di elemento di lavoro", + "sub_issue_count": "Numero di sotto-elementi di lavoro", + "attachment_count": "Numero di allegati", + "created_on": "Creato il", + "sub_issue": "Sotto-elemento di lavoro" + }, + "extra": { + "show_sub_issues": "Mostra sotto-elementi di lavoro", + "show_empty_groups": "Mostra gruppi vuoti" + } + }, + "layouts": { + "ordered_by_label": "Questo layout è ordinato per", + "list": "Lista", + "kanban": "Schede", + "calendar": "Calendario", + "spreadsheet": "Tabella", + "gantt": "Timeline", + "title": { + "list": "Layout a lista", + "kanban": "Layout a schede", + "calendar": "Layout a calendario", + "spreadsheet": "Layout a tabella", + "gantt": "Layout a timeline" + } + }, + "states": { + "active": "Attivo", + "backlog": "Backlog" + }, + "comments": { + "placeholder": "Aggiungi commento", + "switch": { + "private": "Passa a commento privato", + "public": "Passa a commento pubblico" + }, + "create": { + "success": "Commento creato con successo", + "error": "Creazione del commento fallita. Per favore, riprova più tardi." + }, + "update": { + "success": "Commento aggiornato con successo", + "error": "Aggiornamento del commento fallito. Per favore, riprova più tardi." + }, + "remove": { + "success": "Commento rimosso con successo", + "error": "Rimozione del commento fallita. Per favore, riprova più tardi." + }, + "upload": { + "error": "Caricamento dell'asset fallito. Per favore, riprova più tardi." + } + }, + "empty_state": { + "issue_detail": { + "title": "L'elemento di lavoro non esiste", + "description": "L'elemento di lavoro che stai cercando non esiste, è stato archiviato o eliminato.", + "primary_button": { + "text": "Visualizza altri elementi di lavoro" + } + } + }, + "sibling": { + "label": "Elementi di lavoro correlati" + }, + "archive": { + "description": "Solo gli elementi di lavoro completati o annullati possono essere archiviati", + "label": "Archivia elemento di lavoro", + "confirm_message": "Sei sicuro di voler archiviare l'elemento di lavoro? Tutti gli elementi di lavoro archiviati possono essere ripristinati in seguito.", + "success": { + "label": "Archiviazione riuscita", + "message": "I tuoi archivi sono disponibili negli archivi del progetto." + }, + "failed": { + "message": "Impossibile archiviare l'elemento di lavoro. Per favore, riprova." + } + }, + "restore": { + "success": { + "title": "Ripristino riuscito", + "message": "Il tuo elemento di lavoro è disponibile negli elementi del progetto." + }, + "failed": { + "message": "Impossibile ripristinare l'elemento di lavoro. Per favore, riprova." + } + }, + "relation": { + "relates_to": "Collegato a", + "duplicate": "Duplicato di", + "blocked_by": "Bloccato da", + "blocking": "Blocca" + }, + "copy_link": "Copia link dell'elemento di lavoro", + "delete": { + "label": "Elimina elemento di lavoro", + "error": "Errore nell'eliminazione dell'elemento di lavoro" + }, + "subscription": { + "actions": { + "subscribed": "Iscrizione all'elemento di lavoro avvenuta con successo", + "unsubscribed": "Disiscrizione dall'elemento di lavoro avvenuta con successo" + } + }, + "select": { + "error": "Seleziona almeno un elemento di lavoro", + "empty": "Nessun elemento di lavoro selezionato", + "add_selected": "Aggiungi gli elementi di lavoro selezionati" + }, + "open_in_full_screen": "Apri l'elemento di lavoro a schermo intero" + }, + + "attachment": { + "error": "Impossibile allegare il file. Riprova a caricarlo.", + "only_one_file_allowed": "È possibile caricare un solo file alla volta.", + "file_size_limit": "Il file deve essere di {size}MB o meno.", + "drag_and_drop": "Trascina e rilascia ovunque per caricare", + "delete": "Elimina allegato" + }, + + "label": { + "select": "Seleziona etichetta", + "create": { + "success": "Etichetta creata con successo", + "failed": "Creazione dell'etichetta fallita", + "already_exists": "L'etichetta esiste già", + "type": "Digita per aggiungere una nuova etichetta" + } + }, + + "sub_work_item": { + "update": { + "success": "Sotto-elemento di lavoro aggiornato con successo", + "error": "Errore nell'aggiornamento del sotto-elemento di lavoro" + }, + "remove": { + "success": "Sotto-elemento di lavoro rimosso con successo", + "error": "Errore nella rimozione del sotto-elemento di lavoro" + } + }, + + "view": { + "label": "{count, plural, one {Visualizzazione} other {Visualizzazioni}}", + "create": { + "label": "Crea visualizzazione" + }, + "update": { + "label": "Aggiorna visualizzazione" + } + }, + + "inbox_issue": { + "status": { + "pending": { + "title": "In sospeso", + "description": "In sospeso" + }, + "declined": { + "title": "Rifiutato", + "description": "Rifiutato" + }, + "snoozed": { + "title": "Snoozed", + "description": "{days, plural, one {# giorno} other {# giorni}} rimanenti" + }, + "accepted": { + "title": "Accettato", + "description": "Accettato" + }, + "duplicate": { + "title": "Duplicato", + "description": "Duplicato" + } + }, + "modals": { + "decline": { + "title": "Rifiuta elemento di lavoro", + "content": "Sei sicuro di voler rifiutare l'elemento di lavoro {value}?" + }, + "delete": { + "title": "Elimina elemento di lavoro", + "content": "Sei sicuro di voler eliminare l'elemento di lavoro {value}?", + "success": "Elemento di lavoro eliminato con successo" + } + }, + "errors": { + "snooze_permission": "Solo gli amministratori del progetto possono snoozare/non snoozare gli elementi di lavoro", + "accept_permission": "Solo gli amministratori del progetto possono accettare gli elementi di lavoro", + "decline_permission": "Solo gli amministratori del progetto possono rifiutare gli elementi di lavoro" + }, + "actions": { + "accept": "Accetta", + "decline": "Rifiuta", + "snooze": "Snoozed", + "unsnooze": "Annulla snooze", + "copy": "Copia link dell'elemento di lavoro", + "delete": "Elimina", + "open": "Apri elemento di lavoro", + "mark_as_duplicate": "Segna come duplicato", + "move": "Sposta {value} negli elementi di lavoro del progetto" + }, + "source": { + "in-app": "nell'app" + }, + "order_by": { + "created_at": "Creato il", + "updated_at": "Aggiornato il", + "id": "ID" + }, + "label": "Accoglienza", + "page_label": "{workspace} - Accoglienza", + "modal": { + "title": "Crea elemento di lavoro per l'accoglienza" + }, + "tabs": { + "open": "Aperto", + "closed": "Chiuso" + }, + "empty_state": { + "sidebar_open_tab": { + "title": "Nessun elemento di lavoro aperto", + "description": "Trova qui gli elementi di lavoro aperti. Crea un nuovo elemento di lavoro." + }, + "sidebar_closed_tab": { + "title": "Nessun elemento di lavoro chiuso", + "description": "Tutti gli elementi di lavoro, siano essi accettati o rifiutati, possono essere trovati qui." + }, + "sidebar_filter": { + "title": "Nessun elemento di lavoro corrispondente", + "description": "Nessun elemento di lavoro corrisponde al filtro applicato in accoglienza. Crea un nuovo elemento di lavoro." + }, + "detail": { + "title": "Seleziona un elemento di lavoro per visualizzarne i dettagli." + } + } + }, + + "workspace_creation": { + "heading": "Crea il tuo spazio di lavoro", + "subheading": "Per iniziare a usare Plane, devi creare o unirti a uno spazio di lavoro.", + "form": { + "name": { + "label": "Dai un nome al tuo spazio di lavoro", + "placeholder": "Qualcosa di familiare e riconoscibile è sempre meglio." + }, + "url": { + "label": "Imposta l'URL del tuo spazio di lavoro", + "placeholder": "Digita o incolla un URL", + "edit_slug": "Puoi modificare solo lo slug dell'URL" + }, + "organization_size": { + "label": "Quante persone utilizzeranno questo spazio di lavoro?", + "placeholder": "Seleziona una fascia" + } + }, + "errors": { + "creation_disabled": { + "title": "Solo l'amministratore dell'istanza può creare spazi di lavoro", + "description": "Se conosci l'indirizzo email dell'amministratore dell'istanza, clicca il pulsante qui sotto per contattarlo.", + "request_button": "Richiedi all'amministratore dell'istanza" + }, + "validation": { + "name_alphanumeric": "I nomi degli spazi di lavoro possono contenere solo (' '), ('-'), ('_') e caratteri alfanumerici.", + "name_length": "Limita il tuo nome a 80 caratteri.", + "url_alphanumeric": "Gli URL possono contenere solo ('-') e caratteri alfanumerici.", + "url_length": "Limita il tuo URL a 48 caratteri.", + "url_already_taken": "L'URL dello spazio di lavoro è già in uso!" + } + }, + "request_email": { + "subject": "Richiesta per un nuovo spazio di lavoro", + "body": "Ciao amministratore dell'istanza,\n\nPer favore, crea un nuovo spazio di lavoro con l'URL [/nome-spazio] per [scopo del nuovo spazio].\n\nGrazie,\n{firstName} {lastName}\n{email}" + }, + "button": { + "default": "Crea spazio di lavoro", + "loading": "Creazione dello spazio di lavoro in corso" + }, + "toast": { + "success": { + "title": "Successo", + "message": "Spazio di lavoro creato con successo" + }, + "error": { + "title": "Errore", + "message": "Impossibile creare lo spazio di lavoro. Per favore, riprova." + } + } + }, + + "workspace_dashboard": { + "empty_state": { + "general": { + "title": "Panoramica dei tuoi progetti, attività e metriche", + "description": "Benvenuto in Plane, siamo entusiasti di averti qui. Crea il tuo primo progetto e traccia i tuoi elementi di lavoro, e questa pagina si trasformerà in uno spazio che ti aiuta a progredire. Gli amministratori vedranno anche elementi che aiutano il team a progredire.", + "primary_button": { + "text": "Crea il tuo primo progetto", + "comic": { + "title": "Tutto inizia con un progetto in Plane", + "description": "Un progetto può essere la roadmap di un prodotto, una campagna di marketing o il lancio di una nuova auto." + } + } + } + } + }, + + "workspace_analytics": { + "label": "Analisi", + "page_label": "{workspace} - Analisi", + "open_tasks": "Totale attività aperte", + "error": "Si è verificato un errore nel recupero dei dati.", + "work_items_closed_in": "Elementi di lavoro chiusi in", + "selected_projects": "Progetti selezionati", + "total_members": "Totale membri", + "total_cycles": "Totale cicli", + "total_modules": "Totale moduli", + "pending_work_items": { + "title": "Elementi di lavoro in sospeso", + "empty_state": "L'analisi degli elementi di lavoro in sospeso dei colleghi apparirà qui." + }, + "work_items_closed_in_a_year": { + "title": "Elementi di lavoro chiusi in un anno", + "empty_state": "Chiudi gli elementi di lavoro per visualizzare l'analisi sotto forma di grafico." + }, + "most_work_items_created": { + "title": "Maggiori elementi di lavoro creati", + "empty_state": "I colleghi e il numero di elementi di lavoro creati da loro appariranno qui." + }, + "most_work_items_closed": { + "title": "Maggiori elementi di lavoro chiusi", + "empty_state": "I colleghi e il numero di elementi di lavoro chiusi da loro appariranno qui." + }, + "tabs": { + "scope_and_demand": "Ambito e Domanda", + "custom": "Analisi personalizzata" + }, + "empty_state": { + "general": { + "title": "Traccia il progresso, i carichi di lavoro e le assegnazioni. Individua tendenze, rimuovi gli ostacoli e accelera il lavoro", + "description": "Visualizza l'ambito rispetto alla domanda, le stime e il fenomeno del scope creep. Ottieni le prestazioni dei membri del team e dei team, e assicurati che il tuo progetto rispetti le scadenze.", + "primary_button": { + "text": "Inizia il tuo primo progetto", + "comic": { + "title": "Le analisi funzionano meglio con Cicli + Moduli", + "description": "Prima, definisci i tuoi elementi di lavoro in cicli e, se puoi, raggruppa quelli che si estendono per più di un ciclo in moduli. Dai un'occhiata ad entrambi nel menu di sinistra." + } + } + } + } + }, + + "workspace_projects": { + "label": "{count, plural, one {Progetto} other {Progetti}}", + "create": { + "label": "Aggiungi progetto" + }, + "network": { + "label": "Rete", + "private": { + "title": "Privato", + "description": "Accessibile solo su invito" + }, + "public": { + "title": "Pubblico", + "description": "Chiunque nello spazio di lavoro, tranne gli ospiti, può unirsi" + } + }, + "error": { + "permission": "Non hai il permesso di eseguire questa azione.", + "cycle_delete": "Impossibile eliminare il ciclo", + "module_delete": "Impossibile eliminare il modulo", + "issue_delete": "Impossibile eliminare l'elemento di lavoro" + }, + "state": { + "backlog": "Backlog", + "unstarted": "Non iniziato", + "started": "Iniziato", + "completed": "Completato", + "cancelled": "Annullato" + }, + "sort": { + "manual": "Manuale", + "name": "Nome", + "created_at": "Data di creazione", + "members_length": "Numero di membri" + }, + "scope": { + "my_projects": "I miei progetti", + "archived_projects": "Archiviati" + }, + "common": { + "months_count": "{months, plural, one {# mese} other {# mesi}}" + }, + "empty_state": { + "general": { + "title": "Nessun progetto attivo", + "description": "Considera ogni progetto come la base per un lavoro orientato a obiettivi. I progetti sono dove risiedono Jobs, Cicli e Moduli e, insieme ai tuoi colleghi, ti aiutano a raggiungere quell'obiettivo. Crea un nuovo progetto o filtra per progetti archiviati.", + "primary_button": { + "text": "Inizia il tuo primo progetto", + "comic": { + "title": "Tutto inizia con un progetto in Plane", + "description": "Un progetto può essere la roadmap di un prodotto, una campagna di marketing o il lancio di una nuova auto." + } + } + }, + "no_projects": { + "title": "Nessun progetto", + "description": "Per creare elementi di lavoro o gestire il tuo lavoro, devi creare o far parte di un progetto.", + "primary_button": { + "text": "Inizia il tuo primo progetto", + "comic": { + "title": "Tutto inizia con un progetto in Plane", + "description": "Un progetto può essere la roadmap di un prodotto, una campagna di marketing o il lancio di una nuova auto." + } + } + }, + "filter": { + "title": "Nessun progetto corrispondente", + "description": "Nessun progetto rilevato con i criteri di ricerca corrispondenti. \n Crea un nuovo progetto invece." + }, + "search": { + "description": "Nessun progetto rilevato con i criteri di ricerca corrispondenti.\nCrea un nuovo progetto invece" + } + } + }, + + "workspace_views": { + "add_view": "Aggiungi visualizzazione", + "empty_state": { + "all-issues": { + "title": "Nessun elemento di lavoro nel progetto", + "description": "Primo progetto fatto! Ora, suddividi il tuo lavoro in parti tracciabili con gli elementi di lavoro. Andiamo!", + "primary_button": { + "text": "Crea un nuovo elemento di lavoro" + } + }, + "assigned": { + "title": "Nessun elemento di lavoro ancora", + "description": "Gli elementi di lavoro assegnati a te possono essere tracciati da qui.", + "primary_button": { + "text": "Crea un nuovo elemento di lavoro" + } + }, + "created": { + "title": "Nessun elemento di lavoro ancora", + "description": "Tutti gli elementi di lavoro creati da te appariranno qui. Tracciali direttamente da qui.", + "primary_button": { + "text": "Crea un nuovo elemento di lavoro" + } + }, + "subscribed": { + "title": "Nessun elemento di lavoro ancora", + "description": "Iscriviti agli elementi di lavoro che ti interessano, tracciali tutti qui." + }, + "custom-view": { + "title": "Nessun elemento di lavoro ancora", + "description": "Gli elementi di lavoro che corrispondono ai filtri, tracciali tutti qui." + } + } + }, + + "workspace_settings": { + "label": "Impostazioni dello spazio di lavoro", + "page_label": "{workspace} - Impostazioni generali", + "key_created": "Chiave creata", + "copy_key": "Copia e salva questa chiave segreta in Plane Pages. Non potrai vederla dopo aver cliccato Chiudi. È stato scaricato un file CSV contenente la chiave.", + "token_copied": "Token copiato negli appunti.", + "settings": { + "general": { + "title": "Generale", + "upload_logo": "Carica logo", + "edit_logo": "Modifica logo", + "name": "Nome dello spazio di lavoro", + "company_size": "Dimensione aziendale", + "url": "URL dello spazio di lavoro", + "update_workspace": "Aggiorna spazio di lavoro", + "delete_workspace": "Elimina questo spazio di lavoro", + "delete_workspace_description": "Eliminando uno spazio di lavoro, tutti i dati e le risorse all'interno di esso verranno rimossi definitivamente e non potranno essere recuperati.", + "delete_btn": "Elimina questo spazio di lavoro", + "delete_modal": { + "title": "Sei sicuro di voler eliminare questo spazio di lavoro?", + "description": "Hai un periodo di prova attivo per uno dei nostri piani a pagamento. Per procedere, annulla prima il periodo di prova.", + "dismiss": "Annulla", + "cancel": "Annulla periodo di prova", + "success_title": "Spazio di lavoro eliminato.", + "success_message": "Presto verrai reindirizzato alla tua pagina del profilo.", + "error_title": "Qualcosa non ha funzionato.", + "error_message": "Riprova, per favore." + }, + "errors": { + "name": { + "required": "Il nome è obbligatorio", + "max_length": "Il nome dello spazio di lavoro non deve superare gli 80 caratteri" + }, + "company_size": { + "required": "La dimensione aziendale è obbligatoria" + } + } + }, + "members": { + "title": "Membri", + "add_member": "Aggiungi membro", + "pending_invites": "Inviti in sospeso", + "invitations_sent_successfully": "Inviti inviati con successo", + "leave_confirmation": "Sei sicuro di voler lasciare lo spazio di lavoro? Non avrai più accesso a questo spazio. Questa azione non può essere annullata.", + "details": { + "full_name": "Nome completo", + "display_name": "Nome visualizzato", + "email_address": "Indirizzo email", + "account_type": "Tipo di account", + "authentication": "Autenticazione", + "joining_date": "Data di ingresso" + }, + "modal": { + "title": "Invita persone a collaborare", + "description": "Invita persone a collaborare nel tuo spazio di lavoro.", + "button": "Invia inviti", + "button_loading": "Invio inviti in corso", + "placeholder": "nome@azienda.com", + "errors": { + "required": "Abbiamo bisogno di un indirizzo email per invitarli.", + "invalid": "L'email non è valida" + } + } + }, + "billing_and_plans": { + "title": "Fatturazione e Piani", + "current_plan": "Piano attuale", + "free_plan": "Stai attualmente utilizzando il piano gratuito", + "view_plans": "Visualizza piani" + }, + "exports": { + "title": "Esportazioni", + "exporting": "Esportazione in corso", + "previous_exports": "Esportazioni precedenti", + "export_separate_files": "Esporta i dati in file separati", + "modal": { + "title": "Esporta in", + "toasts": { + "success": { + "title": "Esportazione riuscita", + "message": "Potrai scaricare gli {entity} esportati dall'esportazione precedente." + }, + "error": { + "title": "Esportazione fallita", + "message": "L'esportazione non è riuscita. Per favore, riprova." + } + } + } + }, + "webhooks": { + "title": "Webhooks", + "add_webhook": "Aggiungi webhook", + "modal": { + "title": "Crea webhook", + "details": "Dettagli del webhook", + "payload": "URL del payload", + "question": "Quali eventi vuoi attivino questo webhook?", + "error": "L'URL è obbligatorio" + }, + "secret_key": { + "title": "Chiave segreta", + "message": "Genera un token per accedere al payload del webhook" + }, + "options": { + "all": "Inviami tutto", + "individual": "Seleziona eventi individuali" + }, + "toasts": { + "created": { + "title": "Webhook creato", + "message": "Il webhook è stato creato con successo" + }, + "not_created": { + "title": "Webhook non creato", + "message": "Il webhook non può essere creato" + }, + "updated": { + "title": "Webhook aggiornato", + "message": "Il webhook è stato aggiornato con successo" + }, + "not_updated": { + "title": "Webhook non aggiornato", + "message": "Il webhook non può essere aggiornato" + }, + "removed": { + "title": "Webhook rimosso", + "message": "Il webhook è stato rimosso con successo" + }, + "not_removed": { + "title": "Webhook non rimosso", + "message": "Il webhook non può essere rimosso" + }, + "secret_key_copied": { + "message": "Chiave segreta copiata negli appunti." + }, + "secret_key_not_copied": { + "message": "Errore durante la copia della chiave segreta." + } + } + }, + "api_tokens": { + "title": "Token API", + "add_token": "Aggiungi token API", + "create_token": "Crea token", + "never_expires": "Non scade mai", + "generate_token": "Genera token", + "generating": "Generazione in corso", + "delete": { + "title": "Elimina token API", + "description": "Qualsiasi applicazione che utilizza questo token non avrà più accesso ai dati di Plane. Questa azione non può essere annullata.", + "success": { + "title": "Successo!", + "message": "Il token API è stato eliminato con successo" + }, + "error": { + "title": "Errore!", + "message": "Il token API non può essere eliminato" + } + } + } + }, + "empty_state": { + "api_tokens": { + "title": "Nessun token API creato", + "description": "Le API di Plane possono essere utilizzate per integrare i tuoi dati in Plane con qualsiasi sistema esterno. Crea un token per iniziare." + }, + "webhooks": { + "title": "Nessun webhook aggiunto", + "description": "Crea webhook per ricevere aggiornamenti in tempo reale e automatizzare azioni." + }, + "exports": { + "title": "Nessuna esportazione ancora", + "description": "Ogni volta che esporti, avrai anche una copia qui per riferimento." + }, + "imports": { + "title": "Nessuna importazione ancora", + "description": "Trova qui tutte le tue importazioni precedenti e scaricale." + } + } + }, + + "profile": { + "label": "Profilo", + "page_label": "Il tuo lavoro", + "work": "Lavoro", + "details": { + "joined_on": "Iscritto il", + "time_zone": "Fuso orario" + }, + "stats": { + "workload": "Carico di lavoro", + "overview": "Panoramica", + "created": "Elementi di lavoro creati", + "assigned": "Elementi di lavoro assegnati", + "subscribed": "Elementi di lavoro iscritti", + "state_distribution": { + "title": "Elementi di lavoro per stato", + "empty": "Crea elementi di lavoro per visualizzarli per stato nel grafico per un'analisi migliore." + }, + "priority_distribution": { + "title": "Elementi di lavoro per priorità", + "empty": "Crea elementi di lavoro per visualizzarli per priorità nel grafico per un'analisi migliore." + }, + "recent_activity": { + "title": "Attività recente", + "empty": "Non abbiamo trovato dati. Per favore, controlla i tuoi input", + "button": "Scarica l'attività di oggi", + "button_loading": "Download in corso" + } + }, + "actions": { + "profile": "Profilo", + "security": "Sicurezza", + "activity": "Attività", + "appearance": "Aspetto", + "notifications": "Notifiche" + }, + "tabs": { + "summary": "Riepilogo", + "assigned": "Assegnati", + "created": "Creati", + "subscribed": "Iscritti", + "activity": "Attività" + }, + "empty_state": { + "activity": { + "title": "Nessuna attività ancora", + "description": "Inizia creando un nuovo elemento di lavoro! Aggiungi dettagli e proprietà ad esso. Esplora Plane per vedere la tua attività." + }, + "assigned": { + "title": "Nessun elemento di lavoro assegnato a te", + "description": "Gli elementi di lavoro assegnati a te possono essere tracciati da qui." + }, + "created": { + "title": "Nessun elemento di lavoro ancora", + "description": "Tutti gli elementi di lavoro creati da te appariranno qui. Tracciali direttamente da qui." + }, + "subscribed": { + "title": "Nessun elemento di lavoro ancora", + "description": "Iscriviti agli elementi di lavoro che ti interessano, tracciali tutti qui." + } + } + }, + + "project_settings": { + "general": { + "enter_project_id": "Inserisci l'ID del progetto", + "please_select_a_timezone": "Seleziona un fuso orario", + "archive_project": { + "title": "Archivia progetto", + "description": "Archiviare un progetto lo rimuoverà dal menu di navigazione laterale, anche se potrai sempre accedervi dalla pagina dei progetti. Potrai ripristinare il progetto o eliminarlo quando vuoi.", + "button": "Archivia progetto" + }, + "delete_project": { + "title": "Elimina progetto", + "description": "Eliminando un progetto, tutti i dati e le risorse all'interno di esso verranno rimossi definitivamente e non potranno essere recuperati.", + "button": "Elimina il mio progetto" + }, + "toast": { + "success": "Progetto aggiornato con successo", + "error": "Impossibile aggiornare il progetto. Per favore, riprova." + } + }, + "members": { + "label": "Membri", + "project_lead": "Responsabile del progetto", + "default_assignee": "Assegnatario predefinito", + "guest_super_permissions": { + "title": "Concedi accesso in sola lettura a tutti gli elementi di lavoro per gli utenti ospiti:", + "sub_heading": "Questo permetterà agli ospiti di visualizzare tutti gli elementi di lavoro del progetto." + }, + "invite_members": { + "title": "Invita membri", + "sub_heading": "Invita membri a lavorare sul tuo progetto.", + "select_co_worker": "Seleziona un collega" + } + }, + "states": { + "describe_this_state_for_your_members": "Descrivi questo stato per i tuoi membri.", + "empty_state": { + "title": "Nessuno stato disponibile per il gruppo {groupKey}", + "description": "Crea un nuovo stato" + } + }, + "labels": { + "label_title": "Titolo etichetta", + "label_title_is_required": "Il titolo dell'etichetta è obbligatorio", + "label_max_char": "Il nome dell'etichetta non deve superare i 255 caratteri", + "toast": { + "error": "Errore durante l'aggiornamento dell'etichetta" + } + }, + "estimates": { + "title": "Abilita le stime per il mio progetto", + "description": "Aiutano a comunicare la complessità e il carico di lavoro del team." + }, + "automations": { + "label": "Automazioni", + "auto-archive": { + "title": "Archivia automaticamente gli elementi di lavoro chiusi", + "description": "Plane archivierà automaticamente gli elementi di lavoro che sono stati completati o annullati.", + "duration": "Archivia automaticamente gli elementi di lavoro chiusi da" + }, + "auto-close": { + "title": "Chiudi automaticamente gli elementi di lavoro", + "description": "Plane chiuderà automaticamente gli elementi di lavoro che non sono stati completati o annullati.", + "duration": "Chiudi automaticamente gli elementi di lavoro inattivi da", + "auto_close_status": "Stato di chiusura automatica" + } + }, + + "empty_state": { + "labels": { + "title": "Nessuna etichetta ancora", + "description": "Crea etichette per aiutare a organizzare e filtrare gli elementi di lavoro nel tuo progetto." + }, + "estimates": { + "title": "Nessun sistema di stime ancora", + "description": "Crea un set di stime per comunicare la quantità di lavoro per elemento di lavoro.", + "primary_button": "Aggiungi sistema di stime" + } + } + }, + + "project_cycles": { + "add_cycle": "Aggiungi ciclo", + "more_details": "Altri dettagli", + "cycle": "Ciclo", + "update_cycle": "Aggiorna ciclo", + "create_cycle": "Crea ciclo", + "no_matching_cycles": "Nessun ciclo corrispondente", + "remove_filters_to_see_all_cycles": "Rimuovi i filtri per vedere tutti i cicli", + "remove_search_criteria_to_see_all_cycles": "Rimuovi i criteri di ricerca per vedere tutti i cicli", + "only_completed_cycles_can_be_archived": "Solo i cicli completati possono essere archiviati", + "active_cycle": { + "label": "Ciclo attivo", + "progress": "Avanzamento", + "chart": "Grafico di burndown", + "priority_issue": "Elementi di lavoro ad alta priorità", + "assignees": "Assegnatari", + "issue_burndown": "Burndown degli elementi di lavoro", + "ideal": "Ideale", + "current": "Corrente", + "labels": "Etichette" + }, + "upcoming_cycle": { + "label": "Ciclo in arrivo" + }, + "completed_cycle": { + "label": "Ciclo completato" + }, + "status": { + "days_left": "Giorni rimanenti", + "completed": "Completato", + "yet_to_start": "Non ancora iniziato", + "in_progress": "In corso", + "draft": "Bozza" + }, + "action": { + "restore": { + "title": "Ripristina ciclo", + "success": { + "title": "Ciclo ripristinato", + "description": "Il ciclo è stato ripristinato." + }, + "failed": { + "title": "Ripristino del ciclo fallito", + "description": "Il ciclo non può essere ripristinato. Per favore, riprova." + } + }, + "favorite": { + "loading": "Aggiunta del ciclo ai preferiti in corso", + "success": { + "description": "Ciclo aggiunto ai preferiti.", + "title": "Successo!" + }, + "failed": { + "description": "Impossibile aggiungere il ciclo ai preferiti. Per favore, riprova.", + "title": "Errore!" + } + }, + "unfavorite": { + "loading": "Rimozione del ciclo dai preferiti in corso", + "success": { + "description": "Ciclo rimosso dai preferiti.", + "title": "Successo!" + }, + "failed": { + "description": "Impossibile rimuovere il ciclo dai preferiti. Per favore, riprova.", + "title": "Errore!" + } + }, + "update": { + "loading": "Aggiornamento del ciclo in corso", + "success": { + "description": "Ciclo aggiornato con successo.", + "title": "Successo!" + }, + "failed": { + "description": "Errore durante l'aggiornamento del ciclo. Per favore, riprova.", + "title": "Errore!" + }, + "error": { + "already_exists": "Hai già un ciclo nelle date indicate, se vuoi creare una bozza di ciclo, puoi farlo rimuovendo entrambe le date." + } + } + }, + "empty_state": { + "general": { + "title": "Raggruppa e definisci il tempo per il tuo lavoro in cicli.", + "description": "Suddividi il lavoro in blocchi temporali, lavora a ritroso dalla scadenza del tuo progetto per impostare le date e fai progressi tangibili come team.", + "primary_button": { + "text": "Imposta il tuo primo ciclo", + "comic": { + "title": "I cicli sono intervalli temporali ripetitivi.", + "description": "Uno sprint, un'iterazione o qualsiasi altro termine usato per il tracciamento settimanale o bisettimanale del lavoro è un ciclo." + } + } + }, + "no_issues": { + "title": "Nessun elemento di lavoro aggiunto al ciclo", + "description": "Aggiungi o crea gli elementi di lavoro che desideri includere in questo ciclo", + "primary_button": { + "text": "Crea un nuovo elemento di lavoro" + }, + "secondary_button": { + "text": "Aggiungi un elemento di lavoro esistente" + } + }, + "completed_no_issues": { + "title": "Nessun elemento di lavoro nel ciclo", + "description": "Nessun elemento di lavoro presente nel ciclo. Gli elementi di lavoro sono stati trasferiti o nascosti. Per visualizzare gli elementi nascosti, se presenti, aggiorna le proprietà di visualizzazione di conseguenza." + }, + "active": { + "title": "Nessun ciclo attivo", + "description": "Un ciclo attivo è quello che include la data odierna nel suo intervallo. Visualizza qui i dettagli e l'avanzamento del ciclo attivo." + }, + "archived": { + "title": "Nessun ciclo archiviato ancora", + "description": "Per organizzare il tuo progetto, archivia i cicli completati. Li troverai qui una volta archiviati." + } + } + }, + + "project_issues": { + "empty_state": { + "no_issues": { + "title": "Crea un elemento di lavoro e assegnalo a qualcuno, anche a te stesso", + "description": "Considera gli elementi di lavoro come compiti, attività, lavori o JTBD. Un elemento di lavoro e i suoi sotto-elementi di lavoro sono solitamente attività basate sul tempo assegnate ai membri del team. Il tuo team crea, assegna e completa gli elementi di lavoro per portare il progetto verso il suo obiettivo.", + "primary_button": { + "text": "Crea il tuo primo elemento di lavoro", + "comic": { + "title": "Gli elementi di lavoro sono i mattoni fondamentali in Plane.", + "description": "Ridisegna l'interfaccia di Plane, rebranding dell'azienda o lancia il nuovo sistema di iniezione del carburante sono esempi di elementi di lavoro che probabilmente hanno sotto-elementi." + } + } + }, + "no_archived_issues": { + "title": "Nessun elemento di lavoro archiviato ancora", + "description": "Manualmente o tramite automazione, puoi archiviare gli elementi di lavoro che sono stati completati o annullati. Li troverai qui una volta archiviati.", + "primary_button": { + "text": "Imposta l'automazione" + } + }, + "issues_empty_filter": { + "title": "Nessun elemento di lavoro trovato corrispondente ai filtri applicati", + "secondary_button": { + "text": "Cancella tutti i filtri" + } + } + } + }, + + "project_module": { + "add_module": "Aggiungi Modulo", + "update_module": "Aggiorna Modulo", + "create_module": "Crea Modulo", + "archive_module": "Archivia Modulo", + "restore_module": "Ripristina Modulo", + "delete_module": "Elimina modulo", + "empty_state": { + "general": { + "title": "Associa i traguardi del tuo progetto ai Moduli e traccia facilmente il lavoro aggregato.", + "description": "Un gruppo di elementi di lavoro che appartengono a un genitore logico e gerarchico forma un modulo. Considerali come un modo per tracciare il lavoro in base ai traguardi del progetto. Hanno i propri intervalli temporali e scadenze, oltre ad analisi che ti aiutano a vedere quanto sei vicino o lontano da un traguardo.", + "primary_button": { + "text": "Crea il tuo primo modulo", + "comic": { + "title": "I moduli aiutano a raggruppare il lavoro per gerarchia.", + "description": "Un modulo per il carrello, un modulo per il telaio e un modulo per il magazzino sono tutti buoni esempi di questo raggruppamento." + } + } + }, + "no_issues": { + "title": "Nessun elemento di lavoro nel modulo", + "description": "Crea o aggiungi elementi di lavoro che desideri completare come parte di questo modulo", + "primary_button": { + "text": "Crea nuovi elementi di lavoro" + }, + "secondary_button": { + "text": "Aggiungi un elemento di lavoro esistente" + } + }, + "archived": { + "title": "Nessun modulo archiviato ancora", + "description": "Per organizzare il tuo progetto, archivia i moduli completati o annullati. Li troverai qui una volta archiviati." + }, + "sidebar": { + "in_active": "Questo modulo non è ancora attivo.", + "invalid_date": "Data non valida. Inserisci una data valida." + } + }, + "quick_actions": { + "archive_module": "Archivia modulo", + "archive_module_description": "Solo i moduli completati o annullati possono essere archiviati.", + "delete_module": "Elimina modulo" + }, + "toast": { + "copy": { + "success": "Link del modulo copiato negli appunti" + }, + "delete": { + "success": "Modulo eliminato con successo", + "error": "Impossibile eliminare il modulo" + } + } + }, + + "project_views": { + "empty_state": { + "general": { + "title": "Salva visualizzazioni filtrate per il tuo progetto. Crea quante ne vuoi", + "description": "Le visualizzazioni sono un insieme di filtri salvati che usi frequentemente o a cui vuoi avere accesso rapido. Tutti i tuoi colleghi in un progetto possono vedere tutte le visualizzazioni e scegliere quella che fa per loro.", + "primary_button": { + "text": "Crea la tua prima visualizzazione", + "comic": { + "title": "Le visualizzazioni si basano sulle proprietà degli elementi di lavoro.", + "description": "Puoi creare una visualizzazione da qui con quante proprietà e filtri desideri." + } + } + }, + "filter": { + "title": "Nessuna visualizzazione corrispondente", + "description": "Nessuna visualizzazione corrisponde ai criteri di ricerca. \n Crea una nuova visualizzazione invece." + } + } + }, + + "project_page": { + "empty_state": { + "general": { + "title": "Scrivi una nota, un documento o una vera e propria base di conoscenza. Fai partire Galileo, l'assistente AI di Plane, per aiutarti a iniziare", + "description": "Le pagine sono spazi per appunti in Plane. Prendi note durante le riunioni, formattale facilmente, inserisci elementi di lavoro, disponili usando una libreria di componenti e tienili tutti nel contesto del tuo progetto. Per velocizzare qualsiasi documento, invoca Galileo, l'IA di Plane, con una scorciatoia o con il clic di un pulsante.", + "primary_button": { + "text": "Crea la tua prima pagina" + } + }, + "private": { + "title": "Nessuna pagina privata ancora", + "description": "Tieni qui i tuoi appunti privati. Quando sarai pronto a condividerli, il team sarà a portata di clic.", + "primary_button": { + "text": "Crea la tua prima pagina" + } + }, + "public": { + "title": "Nessuna pagina pubblica ancora", + "description": "Visualizza qui le pagine condivise con tutti nel tuo progetto.", + "primary_button": { + "text": "Crea la tua prima pagina" + } + }, + "archived": { + "title": "Nessuna pagina archiviata ancora", + "description": "Archivia le pagine che non sono più di tuo interesse. Potrai accedervi quando necessario." + } + } + }, + + "command_k": { + "empty_state": { + "search": { + "title": "Nessun risultato trovato" + } + } + }, + + "issue_relation": { + "empty_state": { + "search": { + "title": "Nessun elemento di lavoro corrispondente trovato" + }, + "no_issues": { + "title": "Nessun elemento di lavoro trovato" + } + } + }, + + "issue_comment": { + "empty_state": { + "general": { + "title": "Nessun commento ancora", + "description": "I commenti possono essere usati come spazio per discussioni e follow-up sugli elementi di lavoro" + } + } + }, + + "notification": { + "label": "Notifiche", + "page_label": "{workspace} - Notifiche", + "options": { + "mark_all_as_read": "Segna tutto come letto", + "mark_read": "Segna come letto", + "mark_unread": "Segna come non letto", + "refresh": "Aggiorna", + "filters": "Filtri Notifiche", + "show_unread": "Mostra non lette", + "show_snoozed": "Mostra snoozate", + "show_archived": "Mostra archiviate", + "mark_archive": "Archivia", + "mark_unarchive": "Rimuovi da archivio", + "mark_snooze": "Snoozed", + "mark_unsnooze": "Annulla snooze" + }, + "toasts": { + "read": "Notifica segnata come letta", + "unread": "Notifica segnata come non letta", + "archived": "Notifica archiviata", + "unarchived": "Notifica rimossa dall'archivio", + "snoozed": "Notifica snoozata", + "unsnoozed": "Notifica desnoozata" + }, + "empty_state": { + "detail": { + "title": "Seleziona per visualizzare i dettagli." + }, + "all": { + "title": "Nessun elemento di lavoro assegnato", + "description": "Qui puoi vedere gli aggiornamenti degli elementi di lavoro assegnati a te" + }, + "mentions": { + "title": "Nessun elemento di lavoro assegnato", + "description": "Qui puoi vedere gli aggiornamenti degli elementi di lavoro assegnati a te" + } + }, + "tabs": { + "all": "Tutti", + "mentions": "Menzioni" + }, + "filter": { + "assigned": "Assegnati a me", + "created": "Creati da me", + "subscribed": "Iscritti da me" + }, + "snooze": { + "1_day": "1 giorno", + "3_days": "3 giorni", + "5_days": "5 giorni", + "1_week": "1 settimana", + "2_weeks": "2 settimane", + "custom": "Personalizzato" + } + }, + + "active_cycle": { + "empty_state": { + "progress": { + "title": "Aggiungi elementi di lavoro al ciclo per visualizzarne l'avanzamento" + }, + "chart": { + "title": "Aggiungi elementi di lavoro al ciclo per visualizzare il grafico di burndown." + }, + "priority_issue": { + "title": "Visualizza in anteprima gli elementi di lavoro ad alta priorità del ciclo." + }, + "assignee": { + "title": "Aggiungi assegnatari agli elementi di lavoro per vedere la ripartizione per assegnatario." + }, + "label": { + "title": "Aggiungi etichette agli elementi di lavoro per vedere la ripartizione per etichette." + } + } + }, + + "disabled_project": { + "empty_state": { + "inbox": { + "title": "L'accoglienza non è abilitata per il progetto.", + "description": "L'accoglienza ti aiuta a gestire le richieste in entrata per il tuo progetto e ad aggiungerle come elementi di lavoro nel tuo flusso. Abilita l'accoglienza dalle impostazioni del progetto per gestire le richieste.", + "primary_button": { + "text": "Gestisci funzionalità" + } + }, + "cycle": { + "title": "I cicli non sono abilitati per questo progetto.", + "description": "Suddividi il lavoro in blocchi temporali, lavora a ritroso dalla scadenza del tuo progetto per impostare le date e fai progressi tangibili come team. Abilita la funzionalità dei cicli per il tuo progetto per iniziare a usarli.", + "primary_button": { + "text": "Gestisci funzionalità" + } + }, + "module": { + "title": "I moduli non sono abilitati per il progetto.", + "description": "I moduli sono i blocchi costitutivi del tuo progetto. Abilita i moduli dalle impostazioni del progetto per iniziare a usarli.", + "primary_button": { + "text": "Gestisci funzionalità" + } + }, + "page": { + "title": "Le pagine non sono abilitate per il progetto.", + "description": "Le pagine sono i blocchi costitutivi del tuo progetto. Abilita le pagine dalle impostazioni del progetto per iniziare a usarle.", + "primary_button": { + "text": "Gestisci funzionalità" + } + }, + "view": { + "title": "Le visualizzazioni non sono abilitate per il progetto.", + "description": "Le visualizzazioni sono i blocchi costitutivi del tuo progetto. Abilita le visualizzazioni dalle impostazioni del progetto per iniziare a usarle.", + "primary_button": { + "text": "Gestisci funzionalità" + } + } + } + }, + "workspace_draft_issues": { + "draft_an_issue": "Bozza di un elemento di lavoro", + "empty_state": { + "title": "Le bozze degli elementi di lavoro e, presto, anche i commenti appariranno qui.", + "description": "Per provarlo, inizia ad aggiungere un elemento di lavoro e lascialo a metà o crea la tua prima bozza qui sotto. 😉", + "primary_button": { + "text": "Crea la tua prima bozza" + } + }, + "delete_modal": { + "title": "Elimina bozza", + "description": "Sei sicuro di voler eliminare questa bozza? Questa azione non può essere annullata." + }, + "toasts": { + "created": { + "success": "Bozza creata", + "error": "Impossibile creare l'elemento di lavoro. Per favore, riprova." + }, + "deleted": { + "success": "Bozza eliminata" + } + } + }, + + "stickies": { + "title": "I tuoi stickies", + "placeholder": "clicca per scrivere qui", + "all": "Tutti gli stickies", + "no-data": "Annota un'idea, cattura un aha o registra un lampo di genio. Aggiungi uno sticky per iniziare.", + "add": "Aggiungi sticky", + "search_placeholder": "Cerca per titolo", + "delete": "Elimina sticky", + "delete_confirmation": "Sei sicuro di voler eliminare questo sticky?", + "empty_state": { + "simple": "Annota un'idea, cattura un aha o registra un lampo di genio. Aggiungi uno sticky per iniziare.", + "general": { + "title": "Gli stickies sono note rapide e cose da fare che annoti al volo.", + "description": "Cattura i tuoi pensieri e idee senza sforzo creando stickies a cui puoi accedere in qualsiasi momento e ovunque.", + "primary_button": { + "text": "Aggiungi sticky" + } + }, + "search": { + "title": "Non corrisponde a nessuno dei tuoi stickies.", + "description": "Prova con un termine diverso o facci sapere se sei sicuro che la tua ricerca sia corretta.", + "primary_button": { + "text": "Aggiungi sticky" + } + } + }, + "toasts": { + "errors": { + "wrong_name": "Il nome dello sticky non può superare i 100 caratteri.", + "already_exists": "Esiste già uno sticky senza descrizione" + }, + "created": { + "title": "Sticky creato", + "message": "Lo sticky è stato creato con successo" + }, + "not_created": { + "title": "Sticky non creato", + "message": "Lo sticky non può essere creato" + }, + "updated": { + "title": "Sticky aggiornato", + "message": "Lo sticky è stato aggiornato con successo" + }, + "not_updated": { + "title": "Sticky non aggiornato", + "message": "Lo sticky non può essere aggiornato" + }, + "removed": { + "title": "Sticky rimosso", + "message": "Lo sticky è stato rimosso con successo" + }, + "not_removed": { + "title": "Sticky non rimosso", + "message": "Lo sticky non può essere rimosso" + } + } + }, + + "role_details": { + "guest": { + "title": "Ospite", + "description": "I membri esterni alle organizzazioni possono essere invitati come ospiti." + }, + "member": { + "title": "Membro", + "description": "Permette di leggere, scrivere, modificare ed eliminare entità all'interno di progetti, cicli e moduli." + }, + "admin": { + "title": "Amministratore", + "description": "Tutti i permessi impostati su true all'interno dello spazio di lavoro." + } + }, + + "user_roles": { + "product_or_project_manager": "Product / Project Manager", + "development_or_engineering": "Sviluppo / Ingegneria", + "founder_or_executive": "Fondatore / Dirigente", + "freelancer_or_consultant": "Freelance / Consulente", + "marketing_or_growth": "Marketing / Crescita", + "sales_or_business_development": "Vendite / Sviluppo commerciale", + "support_or_operations": "Supporto / Operazioni", + "student_or_professor": "Studente / Professore", + "human_resources": "Risorse umane", + "other": "Altro" + }, + + "importer": { + "github": { + "title": "Github", + "description": "Importa elementi di lavoro dai repository GitHub e sincronizzali." + }, + "jira": { + "title": "Jira", + "description": "Importa elementi di lavoro ed epic dai progetti e dagli epic di Jira." + } + }, + + "exporter": { + "csv": { + "title": "CSV", + "description": "Esporta elementi di lavoro in un file CSV.", + "short_description": "Esporta come CSV" + }, + "excel": { + "title": "Excel", + "description": "Esporta elementi di lavoro in un file Excel.", + "short_description": "Esporta come Excel" + }, + "xlsx": { + "title": "Excel", + "description": "Esporta elementi di lavoro in un file Excel.", + "short_description": "Esporta come Excel" + }, + "json": { + "title": "JSON", + "description": "Esporta elementi di lavoro in un file JSON.", + "short_description": "Esporta come JSON" + } + }, + "default_global_view": { + "all_issues": "Tutti gli elementi di lavoro", + "assigned": "Assegnati", + "created": "Creati", + "subscribed": "Iscritti" + }, + + "themes": { + "theme_options": { + "system_preference": { + "label": "Preferenza di sistema" + }, + "light": { + "label": "Chiaro" + }, + "dark": { + "label": "Scuro" + }, + "light_contrast": { + "label": "Contrasto elevato chiaro" + }, + "dark_contrast": { + "label": "Contrasto elevato scuro" + }, + "custom": { + "label": "Tema personalizzato" + } + } + }, + "project_modules": { + "status": { + "backlog": "Backlog", + "planned": "Pianificato", + "in_progress": "In corso", + "paused": "In pausa", + "completed": "Completato", + "cancelled": "Annullato" + }, + "layout": { + "list": "Layout a lista", + "board": "Layout a galleria", + "timeline": "Layout a timeline" + }, + "order_by": { + "name": "Nome", + "progress": "Avanzamento", + "issues": "Numero di elementi di lavoro", + "due_date": "Scadenza", + "created_at": "Data di creazione", + "manual": "Manuale" + } + }, + + "cycle": { + "label": "{count, plural, one {Ciclo} other {Cicli}}", + "no_cycle": "Nessun ciclo" + }, + + "module": { + "label": "{count, plural, one {Modulo} other {Moduli}}", + "no_module": "Nessun modulo" + } +} diff --git a/packages/i18n/src/locales/ja/translations.json b/packages/i18n/src/locales/ja/translations.json index eec78decc..bbc6ff759 100644 --- a/packages/i18n/src/locales/ja/translations.json +++ b/packages/i18n/src/locales/ja/translations.json @@ -631,6 +631,8 @@ "states": "ステータス", "state": "ステータス", "state_groups": "ステータスグループ", + "state_group": "ステート グループ", + "priorities": "優先度", "priority": "優先度", "team_project": "チームプロジェクト", "project": "プロジェクト", @@ -639,12 +641,16 @@ "module": "モジュール", "modules": "モジュール", "labels": "ラベル", + "label": "ラベル", "assignees": "担当者", "assignee": "担当者", "created_by": "作成者", "none": "なし", "link": "リンク", + "estimates": "見積もり", "estimate": "見積もり", + "created_at": "クリエイテッド アット", + "completed_at": "コンプリーテッド アット", "layout": "レイアウト", "filters": "フィルター", "display": "表示", @@ -687,7 +693,6 @@ "add_more": "さらに追加", "defaults": "デフォルト", "add_label": "ラベルを追加", - "estimates": "見積もり", "customize_time_range": "期間をカスタマイズ", "loading": "読み込み中", "attachments": "添付ファイル", @@ -825,8 +830,6 @@ "select": "選択", "upgrade": "アップグレード", "add_seats": "シートを追加", - "label": "ラベル", - "priorities": "優先度", "projects": "プロジェクト", "workspace": "ワークスペース", "workspaces": "ワークスペース", diff --git a/packages/i18n/src/locales/ru/translations.json b/packages/i18n/src/locales/ru/translations.json new file mode 100644 index 000000000..21c8a6e78 --- /dev/null +++ b/packages/i18n/src/locales/ru/translations.json @@ -0,0 +1,2365 @@ +{ + "sidebar": { + "projects": "Проекты", + "pages": "Страницы", + "new_work_item": "Новый рабочий элемент", + "home": "Главная", + "your_work": "Ваша работа", + "inbox": "Входящие", + "workspace": "Рабочие пространства", + "views": "Представления", + "analytics": "Аналитика", + "work_items": "Рабочие элементы", + "cycles": "Циклы", + "modules": "Модули", + "intake": "Предложения", + "drafts": "Черновики", + "favorites": "Избранное", + "pro": "Pro", + "upgrade": "Обновить" + }, + "auth": { + "common": { + "email": { + "label": "Email", + "placeholder": "name@company.com", + "errors": { + "required": "Email обязателен", + "invalid": "Email недействителен" + } + }, + "password": { + "label": "Пароль", + "set_password": "Установить пароль", + "placeholder": "Введите пароль", + "confirm_password": { + "label": "Подтвердите пароль", + "placeholder": "Подтвердите пароль" + }, + "current_password": { + "label": "Текущий пароль" + }, + "new_password": { + "label": "Новый пароль", + "placeholder": "Введите новый пароль" + }, + "change_password": { + "label": { + "default": "Сменить пароль", + "submitting": "Смена пароля" + } + }, + "errors": { + "match": "Пароли не совпадают", + "empty": "Пожалуйста, введите ваш пароль", + "length": "Длина пароля должна быть более 8 символов", + "strength": { + "weak": "Слабый пароль", + "strong": "Сильный пароль" + } + }, + "submit": "Установить пароль", + "toast": { + "change_password": { + "success": { + "title": "Успех!", + "message": "Пароль успешно изменён." + }, + "error": { + "title": "Ошибка!", + "message": "Что-то пошло не так. Пожалуйста, попробуйте снова." + } + } + } + }, + "unique_code": { + "label": "Уникальный код", + "placeholder": "gets-sets-flys", + "paste_code": "Вставьте код, отправленный на ваш email", + "requesting_new_code": "Запрос нового кода", + "sending_code": "Отправка кода" + }, + "already_have_an_account": "Уже есть аккаунт?", + "login": "Войти", + "create_account": "Создать аккаунт", + "new_to_plane": "Впервые в Plane?", + "back_to_sign_in": "Вернуться к входу", + "resend_in": "Отправить снова через {seconds} секунд", + "sign_in_with_unique_code": "Войти с уникальным кодом", + "forgot_password": "Забыли пароль?" + }, + "sign_up": { + "header": { + "label": "Создайте аккаунт, чтобы начать управлять работой с вашей командой.", + "step": { + "email": { + "header": "Регистрация", + "sub_header": "" + }, + "password": { + "header": "Регистрация", + "sub_header": "Зарегистрируйтесь, используя комбинацию email-пароль." + }, + "unique_code": { + "header": "Регистрация", + "sub_header": "Зарегистрируйтесь, используя уникальный код, отправленный на указанный выше email." + } + } + }, + "errors": { + "password": { + "strength": "Попробуйте установить сильный пароль для продолжения" + } + } + }, + "sign_in": { + "header": { + "label": "Войдите, чтобы начать управлять работой с вашей командой.", + "step": { + "email": { + "header": "Войти или зарегистрироваться", + "sub_header": "" + }, + "password": { + "header": "Войти или зарегистрироваться", + "sub_header": "Используйте комбинацию email-пароль для входа." + }, + "unique_code": { + "header": "Войти или зарегистрироваться", + "sub_header": "Войдите, используя уникальный код, отправленный на указанный выше email." + } + } + } + }, + "forgot_password": { + "title": "Сбросьте ваш пароль", + "description": "Введите проверенный email вашего аккаунта, и мы отправим вам ссылку для сброса пароля.", + "email_sent": "Мы отправили ссылку для сброса на ваш email", + "send_reset_link": "Отправить ссылку для сброса", + "errors": { + "smtp_not_enabled": "Мы видим, что ваш администратор не включил SMTP, мы не сможем отправить ссылку для сброса пароля" + }, + "toast": { + "success": { + "title": "Email отправлен", + "message": "Проверьте ваши входящие для ссылки на сброс пароля. Если она не появится в течение нескольких минут, проверьте папку спама." + }, + "error": { + "title": "Ошибка!", + "message": "Что-то пошло не так. Пожалуйста, попробуйте снова." + } + } + }, + "reset_password": { + "title": "Установите новый пароль", + "description": "Обеспечьте безопасность вашего аккаунта с помощью сильного пароля" + }, + "set_password": { + "title": "Обеспечьте безопасность вашего аккаунта", + "description": "Установка пароля помогает вам безопасно входить в систему" + }, + "sign_out": { + "toast": { + "error": { + "title": "Ошибка!", + "message": "Не удалось выйти. Пожалуйста, попробуйте снова." + } + } + } + }, + "submit": "Отправить", + "cancel": "Отменить", + "loading": "Загрузка", + "error": "Ошибка", + "success": "Успешно", + "warning": "Предупреждение", + "info": "Информация", + "close": "Закрыть", + "yes": "Да", + "no": "Нет", + "ok": "OK", + "name": "Имя", + "description": "Описание", + "search": "Поиск", + "add_member": "Добавить участника", + "adding_members": "Добавление участников", + "remove_member": "Удалить участника", + "add_members": "Добавить участников", + "adding_member": "Добавление участников", + "remove_members": "Удалить участников", + "add": "Добавить", + "adding": "Добавление", + "remove": "Удалить", + "add_new": "Добавить новый", + "remove_selected": "Удалить выбранное", + "first_name": "Имя", + "last_name": "Фамилия", + "email": "Email", + "display_name": "Отображаемое имя", + "role": "Роль", + "timezone": "Часовой пояс", + "avatar": "Аватар", + "cover_image": "Обложка", + "password": "Пароль", + "change_cover": "Изменить обложку", + "language": "Язык", + "saving": "Сохранение", + "save_changes": "Сохранить изменения", + "deactivate_account": "Деактивировать аккаунт", + "deactivate_account_description": "При деактивации аккаунта все данные и ресурсы будут безвозвратно удалены и не могут быть восстановлены.", + "profile_settings": "Настройки профиля", + "your_account": "Ваш аккаунт", + "security": "Безопасность", + "activity": "Активность", + "appearance": "Внешний вид", + "notifications": "Уведомления", + "workspaces": "Рабочие пространства", + "create_workspace": "Создать рабочее пространство", + "invitations": "Приглашения", + "summary": "Сводка", + "assigned": "Назначено", + "created": "Создано", + "subscribed": "Подписались", + "you_do_not_have_the_permission_to_access_this_page": "У вас нет прав для доступа к этой странице.", + "something_went_wrong_please_try_again": "Что-то пошло не так. Пожалуйста, попробуйте еще раз.", + "load_more": "Загрузить еще", + "select_or_customize_your_interface_color_scheme": "Выберите или настройте цветовую схему интерфейса.", + "theme": "Тема", + "system_preference": "Системные настройки", + "light": "Светлая", + "dark": "Темная", + "light_contrast": "Светлая высококонтрастная", + "dark_contrast": "Темная высококонтрастностная", + "custom": "Пользовательская тема", + "select_your_theme": "Выберите тему", + "customize_your_theme": "Настройте свою тему", + "background_color": "Цвет фона", + "text_color": "Цвет текста", + "primary_color": "Основной цвет (темы)", + "sidebar_background_color": "Цвет фона боковой панели", + "sidebar_text_color": "Цвет текста боковой панели", + "set_theme": "Установить тему", + "enter_a_valid_hex_code_of_6_characters": "Введите корректный HEX-код из 6 символов", + "background_color_is_required": "Требуется цвет фона", + "text_color_is_required": "Требуется цвет текста", + "primary_color_is_required": "Требуется основной цвет", + "sidebar_background_color_is_required": "Требуется цвет фона боковой панели", + "sidebar_text_color_is_required": "Требуется цвет текста боковой панели", + "updating_theme": "Обновление темы", + "theme_updated_successfully": "Тема успешно обновлена", + "failed_to_update_the_theme": "Не удалось обновить тему", + "email_notifications": "Email-уведомления", + "stay_in_the_loop_on_issues_you_are_subscribed_to_enable_this_to_get_notified": "Будьте в курсе рабочих элементов, на которые вы подписаны. Включите уведомления.", + "email_notification_setting_updated_successfully": "Настройки email-уведомлений успешно обновлены", + "failed_to_update_email_notification_setting": "Не удалось обновить настройки email-уведомлений", + "notify_me_when": "Уведомлять меня, когда", + "property_changes": "Изменения свойств", + "property_changes_description": "Уведомлять при изменении свойств рабочих элементов: назначенные, приоритет, оценки и прочее", + "state_change": "Изменение статуса", + "state_change_description": "Уведомлять при изменении статуса рабочего элемента", + "issue_completed": "Рабочий элемент выполнен", + "issue_completed_description": "Уведомлять только при выполнении рабочего элемента", + "comments": "Комментарии", + "comments_description": "Уведомлять при добавлении комментариев к рабочему элементу", + "mentions": "Упоминания", + "mentions_description": "Уведомлять только при упоминании меня в комментариях или описании", + "old_password": "Старый пароль", + "general_settings": "Общие настройки", + "sign_out": "Выйти", + "signing_out": "Выход...", + "active_cycles": "Активные циклы", + "active_cycles_description": "Мониторинг циклов по проектам, отслеживание приоритетных рабочих элементов и фокусировка на проблемных циклах.", + "on_demand_snapshots_of_all_your_cycles": "Моментальные снимки всех ваших циклов", + "upgrade": "Обновить", + "10000_feet_view": "Обзор всех активных циклов с высоты", + "10000_feet_view_description": "Общий обзор выполняющихся циклов во всех проектах вместо переключения между циклами в каждом проекте.", + "get_snapshot_of_each_active_cycle": "Получить снимок каждого активного цикла", + "get_snapshot_of_each_active_cycle_description": "Отслеживайте ключевые метрики активных циклов, их прогресс и соответствие срокам.", + "compare_burndowns": "Сравнение графиков выгорания", + "compare_burndowns_description": "Мониторинг производительности команд через анализ графиков выполнения циклов.", + "quickly_see_make_or_break_issues": "Быстрый просмотр критических рабочих элементов", + "quickly_see_make_or_break_issues_description": "Просмотр высокоприоритетных рабочих элементов с указанием сроков для каждого цикла в один клик.", + "zoom_into_cycles_that_need_attention": "Фокусировка на проблемных циклах", + "zoom_into_cycles_that_need_attention_description": "Исследование состояния циклов, не соответствующих ожиданиям, в один клик.", + "stay_ahead_of_blockers": "Предупреждение блокирующих рабочих элементов", + "stay_ahead_of_blockers_description": "Выявление проблем между проектами и скрытых зависимостей между циклами.", + "analytics": "Аналитика", + "workspace_invites": "Приглашения в рабочее пространство", + "enter_god_mode": "Режим администратора", + "workspace_logo": "Логотип рабочего пространства", + "new_issue": "Новый рабочий элемент", + "your_work": "Ваша работа", + "drafts": "Черновики", + "projects": "Проекты", + "views": "Представления", + "workspace": "Рабочее пространство", + "archives": "Архивы", + "settings": "Настройки", + "failed_to_move_favorite": "Ошибка перемещения избранного", + "favorites": "Избранное", + "no_favorites_yet": "Нет избранного", + "create_folder": "Создать папку", + "new_folder": "Новая папка", + "favorite_updated_successfully": "Избранное успешно обновлено", + "favorite_created_successfully": "Избранное успешно создано", + "folder_already_exists": "Папка уже существует", + "folder_name_cannot_be_empty": "Имя папки не может быть пустым", + "something_went_wrong": "Что-то пошло не так", + "failed_to_reorder_favorite": "Ошибка изменения порядка избранного", + "favorite_removed_successfully": "Избранное успешно удалено", + "failed_to_create_favorite": "Ошибка создания избранного", + "failed_to_rename_favorite": "Ошибка переименования избранного", + "project_link_copied_to_clipboard": "Ссылка на проект скопирована в буфер обмена", + "link_copied": "Ссылка скопирована", + "add_project": "Добавить проект", + "create_project": "Создать проект", + "failed_to_remove_project_from_favorites": "Не удалось удалить проект из избранного. Попробуйте снова.", + "project_created_successfully": "Проект успешно создан", + "project_created_successfully_description": "Проект успешно создан. Теперь вы можете добавлять рабочие элементы.", + "project_cover_image_alt": "Обложка проекта", + "name_is_required": "Требуется имя", + "title_should_be_less_than_255_characters": "Заголовок должен быть короче 255 символов", + "project_name": "Название проекта", + "project_id_must_be_at_least_1_character": "ID проекта должен содержать минимум 1 символ", + "project_id_must_be_at_most_5_characters": "ID проекта должен содержать максимум 5 символов", + "project_id": "ID проекта", + "project_id_tooltip_content": "Помогает идентифицировать рабочие элементы в проекте. Макс. 5 символов.", + "description_placeholder": "Описание", + "only_alphanumeric_non_latin_characters_allowed": "Допускаются только буквенно-цифровые и нелатинские символы.", + "project_id_is_required": "Требуется ID проекта", + "project_id_allowed_char": "Допускаются только буквенно-цифровые и нелатинские символы.", + "project_id_min_char": "ID проекта должен содержать минимум 1 символ", + "project_id_max_char": "ID проекта должен содержать максимум 5 символов", + "project_description_placeholder": "Введите описание проекта", + "select_network": "Выбрать сеть", + "lead": "Руководитель", + "date_range": "Диапазон дат", + "private": "Приватный", + "public": "Публичный", + "accessible_only_by_invite": "Доступ только по приглашению", + "anyone_in_the_workspace_except_guests_can_join": "Все участники рабочего пространства (кроме гостей) могут присоединиться", + "creating": "Создание", + "creating_project": "Создание проекта", + "adding_project_to_favorites": "Добавление проекта в избранное", + "project_added_to_favorites": "Проект добавлен в избранное", + "couldnt_add_the_project_to_favorites": "Не удалось добавить проект в избранное. Попробуйте снова.", + "removing_project_from_favorites": "Удаление проекта из избранного", + "project_removed_from_favorites": "Проект удален из избранного", + "couldnt_remove_the_project_from_favorites": "Не удалось удалить проект из избранного. Попробуйте снова.", + "add_to_favorites": "Добавить в избранное", + "remove_from_favorites": "Удалить из избранного", + "publish_settings": "Настройки публикации", + "publish": "Опубликовать", + "copy_link": "Копировать ссылку", + "leave_project": "Покинуть проект", + "join_the_project_to_rearrange": "Присоединитесь к проекту для изменения порядка", + "drag_to_rearrange": "Перетащите для изменения порядка", + "congrats": "Поздравляем!", + "open_project": "Открыть проект", + "issues": "Рабочие элементы", + "cycles": "Циклы", + "modules": "Модули", + "pages": "Страницы", + "intake": "Предложения", + "time_tracking": "Учет времени", + "work_management": "Управление рабочими элементами", + "projects_and_issues": "Проекты и рабочие элементы", + "projects_and_issues_description": "Включить/отключить для этого проекта", + "cycles_description": "Группировка рабочих элементов по временным интервалам с возможностью изменения периодичности.", + "modules_description": "Группировка рабочих элементов в подпроекты с собственными руководителями и назначенными.", + "views_description": "Сохранение сортировок, фильтров и отображений для повторного использования или совместного доступа.", + "pages_description": "Создание любых текстовых материалов", + "intake_description": "Отслеживание рабочих элементов, на которые вы подписаны. Включите для получения уведомлений.", + "time_tracking_description": "Учет времени, затраченного на рабочие элементы и проекты.", + "work_management_description": "Управление рабочими элементами и проектами", + "documentation": "Документация", + "message_support": "Написать в поддержку", + "contact_sales": "Связаться с отделом продаж", + "hyper_mode": "Гиперрежим", + "keyboard_shortcuts": "Горячие клавиши", + "whats_new": "Что нового?", + "version": "Версия", + "we_are_having_trouble_fetching_the_updates": "Возникли проблемы с получением обновлений.", + "our_changelogs": "наши журналы изменений", + "for_the_latest_updates": "для последних обновлений.", + "please_visit": "Пожалуйста, посетите", + "docs": "Документация", + "full_changelog": "Полный журнал изменений", + "support": "Поддержка", + "discord": "Discord", + "powered_by_plane_pages": "Работает на Plane Pages", + "please_select_at_least_one_invitation": "Пожалуйста, выберите хотя бы одно приглашение.", + "please_select_at_least_one_invitation_description": "Для присоединения к рабочему пространству выберите хотя бы одно приглашение.", + "we_see_that_someone_has_invited_you_to_join_a_workspace": "Вы получили приглашение присоединиться к рабочему пространству", + "join_a_workspace": "Присоединиться к рабочему пространству", + "we_see_that_someone_has_invited_you_to_join_a_workspace_description": "Вы получили приглашение присоединиться к рабочему пространству", + "join_a_workspace_description": "Присоединиться к рабочему пространству", + "accept_and_join": "Принять и присоединиться", + "go_home": "На главную", + "no_pending_invites": "Нет ожидающих приглашений", + "you_can_see_here_if_someone_invites_you_to_a_workspace": "Здесь отображаются приглашения в рабочие пространства", + "back_to_home": "Вернуться на главную", + "workspace_name": "название-рабочего-пространства", + "deactivate_your_account": "Деактивировать ваш аккаунт", + "deactivate_your_account_description": "После деактивации вы не сможете получать рабочие элементы и оплачивать рабочее пространство. Для реактивации потребуется новое приглашение.", + "deactivating": "Деактивация...", + "confirm": "Подтвердить", + "confirming": "Подтверждение...", + "draft_created": "Черновик создан", + "issue_created_successfully": "Рабочий элемент успешно создан", + "draft_creation_failed": "Ошибка создания черновика", + "issue_creation_failed": "Ошибка создания рабочего элемента", + "draft_issue": "Черновик рабочего элемента", + "issue_updated_successfully": "Рабочий элемент успешно обновлен", + "issue_could_not_be_updated": "Не удалось обновить рабочий элемент", + "create_a_draft": "Создать черновик", + "save_to_drafts": "Сохранить в черновики", + "save": "Сохранить", + "update": "Обновить", + "updating": "Обновление...", + "create_new_issue": "Создать новый рабочий элемент", + "editor_is_not_ready_to_discard_changes": "Редактор не готов отменить изменения", + "failed_to_move_issue_to_project": "Ошибка перемещения рабочего элемента в проект", + "create_more": "Создать еще", + "add_to_project": "Добавить в проект", + "discard": "Отменить", + "duplicate_issue_found": "Найден дублирующийся рабочий элемент", + "duplicate_issues_found": "Найдены дублирующиеся рабочие элементы", + "no_matching_results": "Нет совпадений", + "title_is_required": "Требуется заголовок", + "title": "Заголовок", + "state": "Статус", + "priority": "Приоритет", + "none": "Нет", + "urgent": "Срочный", + "high": "Высокий", + "medium": "Средний", + "low": "Низкий", + "members": "Участники", + "assignee": "Назначенный", + "assignees": "Назначенные", + "you": "Вы", + "labels": "Метки", + "create_new_label": "Создать новую метку", + "start_date": "Дата начала", + "end_date": "Дата окончания", + "due_date": "Срок выполнения", + "estimate": "Оценка", + "change_parent_issue": "Изменить родительский рабочий элемент", + "remove_parent_issue": "Удалить родительский рабочий элемент", + "add_parent": "Добавить родительский", + "loading_members": "Загрузка участников", + "view_link_copied_to_clipboard": "Ссылка на представление скопирована в буфер", + "required": "Обязательно", + "optional": "Опционально", + "Cancel": "Отмена", + "edit": "Редактировать", + "archive": "Архивировать", + "restore": "Восстановить", + "open_in_new_tab": "Открыть в новой вкладке", + "delete": "Удалить", + "deleting": "Удаление...", + "make_a_copy": "Создать копию", + "move_to_project": "Переместить в проект", + "good": "Доброго", + "morning": "утра", + "afternoon": "дня", + "evening": "вечера", + "show_all": "Показать все", + "show_less": "Свернуть", + "no_data_yet": "Нет данных", + "syncing": "Синхронизация", + "add_work_item": "Добавить рабочий элемент", + "advanced_description_placeholder": "Нажмите '/' для команд", + "create_work_item": "Создать рабочий элемент", + "attachments": "Вложения", + "declining": "Отмена...", + "declined": "Отменено", + "decline": "Отменить", + "unassigned": "Не назначено", + "work_items": "Рабочие элементы", + "add_link": "Добавить ссылку", + "points": "Очки", + "no_assignee": "Нет назначенного", + "no_assignees_yet": "Нет назначенных", + "no_labels_yet": "Нет меток", + "ideal": "Идеально", + "current": "Текущее", + "no_matching_members": "Нет совпадений", + "leaving": "Выход...", + "removing": "Удаление...", + "leave": "Покинуть", + "refresh": "Обновить", + "refreshing": "Обновление...", + "refresh_status": "Обновить статус", + "prev": "Назад", + "next": "Вперед", + "re_generating": "Повторная генерация...", + "re_generate": "Сгенерировать заново", + "re_generate_key": "Перегенерировать ключ", + "export": "Экспорт", + "member": "{count, plural, one{# участник} few{# участника} other{# участников}}", + + "project_view": { + "sort_by": { + "created_at": "Дата создания", + "updated_at": "Дата обновления", + "name": "Имя" + } + }, + + "toast": { + "success": "Успех!", + "error": "Ошибка!" + }, + + "links": { + "toasts": { + "created": { + "title": "Ссылка создана", + "message": "Ссылка успешно создана" + }, + "not_created": { + "title": "Ошибка создания ссылки", + "message": "Не удалось создать ссылку" + }, + "updated": { + "title": "Ссылка обновлена", + "message": "Ссылка успешно обновлена" + }, + "not_updated": { + "title": "Ошибка обновления ссылки", + "message": "Не удалось обновить ссылку" + }, + "removed": { + "title": "Ссылка удалена", + "message": "Ссылка успешно удалена" + }, + "not_removed": { + "title": "Ошибка удаления ссылки", + "message": "Не удалось удалить ссылку" + } + } + }, + + "home": { + "empty": { + "quickstart_guide": "Руководство по началу работы", + "not_right_now": "Не сейчас", + "create_project": { + "title": "Создать проект", + "description": "Большинство вещей начинаются с проекта в Plane.", + "cta": "Начать" + }, + "invite_team": { + "title": "Пригласить команду", + "description": "Создавайте, развивайте и управляйте вместе с коллегами.", + "cta": "Пригласить" + }, + "configure_workspace": { + "title": "Настройте рабочее пространство", + "description": "Включайте/отключайте функции или делайте больше.", + "cta": "Настроить" + }, + "personalize_account": { + "title": "Персонализируйте Plane", + "description": "Выберите изображение, цвета и другие параметры.", + "cta": "Настроить сейчас" + }, + "widgets": { + "title": "Включите виджеты для лучшего опыта", + "description": "Все ваши виджеты выключены. Включите их\nдля улучшения взаимодействия!", + "primary_button": { + "text": "Управление виджетами" + } + } + }, + "quick_links": { + "empty": "Сохраняйте ссылки на важные рабочие элементы.", + "add": "Добавить быструю ссылку", + "title": "Быстрая ссылка", + "title_plural": "Быстрые ссылки" + }, + "recents": { + "title": "Недавние", + "empty": { + "project": "Недавние проекты появятся здесь после посещения.", + "page": "Недавние страницы появятся здесь после посещения.", + "issue": "Недавние рабочие элементы появятся здесь после посещения.", + "default": "Пока нет недавних элементов" + }, + "filters": { + "all": "Все элементы", + "projects": "Проекты", + "pages": "Страницы", + "issues": "Рабочие элементы" + } + }, + "new_at_plane": { + "title": "Новое в Plane" + }, + "quick_tutorial": { + "title": "Быстрое обучение" + }, + "widget": { + "reordered_successfully": "Виджет успешно перемещен.", + "reordering_failed": "Ошибка при изменении порядка виджетов." + }, + "manage_widgets": "Управление виджетами", + "title": "Главная", + "star_us_on_github": "Оцените нас на GitHub" + }, + + "link": { + "modal": { + "url": { + "text": "URL", + "required": "Некорректный URL", + "placeholder": "Введите или вставьте URL" + }, + "title": { + "text": "Отображаемый заголовок", + "placeholder": "Как вы хотите видеть эту ссылку" + } + } + }, + + "common": { + "all": "Все", + "states": "Статусы", + "state": "Статус", + "state_groups": "Группы статусов", + "state_group": "Группа статусов", + "priorities": "Приоритеты", + "priority": "Приоритет", + "team_project": "Командный проект", + "project": "Проект", + "cycle": "Цикл", + "cycles": "Циклы", + "module": "Модуль", + "modules": "Модули", + "labels": "Метки", + "label": "Метка", + "assignees": "Назначенные", + "assignee": "Назначенный", + "created_by": "Создано", + "none": "Нет", + "link": "Ссылка", + "estimates": "Оценки", + "estimate": "Оценка", + "created_at": "Создано в", + "completed_at": "Завершено в", + "layout": "Макет", + "filters": "Фильтры", + "display": "Отображение", + "load_more": "Загрузить еще", + "activity": "Активность", + "analytics": "Аналитика", + "dates": "Даты", + "success": "Успешно!", + "something_went_wrong": "Что-то пошло не так", + "error": { + "label": "Ошибка!", + "message": "Произошла ошибка. Пожалуйста, попробуйте снова." + }, + "group_by": "Группировать по", + "epic": "Эпик", + "epics": "Эпики", + "work_item": "Рабочий элемент", + "work_items": "Рабочие элементы", + "sub_work_item": "Подэлемент", + "add": "Добавить", + "warning": "Предупреждение", + "updating": "Обновление", + "adding": "Добавление", + "update": "Обновить", + "creating": "Создание", + "create": "Создать", + "cancel": "Отмена", + "description": "Описание", + "title": "Заголовок", + "attachment": "Вложение", + "general": "Общее", + "features": "Функции", + "automation": "Автоматизация", + "project_name": "Название проекта", + "project_id": "ID проекта", + "project_timezone": "Часовой пояс проекта", + "created_on": "Создано", + "update_project": "Обновить проект", + "identifier_already_exists": "Идентификатор уже существует", + "add_more": "Добавить еще", + "defaults": "По умолчанию", + "add_label": "Добавить метку", + "customize_time_range": "Настроить период", + "loading": "Загрузка", + "attachments": "Вложения", + "property": "Свойство", + "properties": "Свойства", + "parent": "Родительский", + "remove": "Удалить", + "archiving": "Архивация", + "archive": "Архивировать", + "access": { + "public": "Публичный", + "private": "Приватный" + }, + "done": "Готово", + "sub_work_items": "Подэлементы", + "comment": "Комментарий", + "workspace_level": "Уровень рабочего пространства", + "order_by": { + "label": "Сортировать по", + "manual": "Вручную", + "last_created": "Последние созданные", + "last_updated": "Последние обновленные", + "start_date": "Дата начала", + "due_date": "Срок выполнения", + "asc": "По возрастанию", + "desc": "По убыванию", + "updated_on": "Дата обновления" + }, + "sort": { + "asc": "По возрастанию", + "desc": "По убыванию", + "created_on": "Дата создания", + "updated_on": "Дата обновления" + }, + "comments": "Комментарии", + "updates": "Обновления", + "clear_all": "Очистить все", + "copied": "Скопировано!", + "link_copied": "Ссылка скопирована!", + "link_copied_to_clipboard": "Ссылка скопирована в буфер обмена", + "copied_to_clipboard": "Ссылка на рабочий элемент скопирована", + "is_copied_to_clipboard": "Рабочий элемент скопирован в буфер обмена", + "no_links_added_yet": "Нет добавленных ссылок", + "add_link": "Добавить ссылку", + "links": "Ссылки", + "go_to_workspace": "Перейти в рабочее пространство", + "progress": "Прогресс", + "optional": "Опционально", + "join": "Присоединиться", + "go_back": "Назад", + "continue": "Продолжить", + "resend": "Отправить повторно", + "relations": "Связи", + "errors": { + "default": { + "title": "Ошибка!", + "message": "Что-то пошло не так. Попробуйте позже." + }, + "required": "Это поле обязательно", + "entity_required": "{entity} обязательно" + }, + "update_link": "обновить ссылку", + "attach": "Прикрепить", + "create_new": "Создать новый", + "add_existing": "Добавить существующий", + "type_or_paste_a_url": "Введите или вставьте URL", + "url_is_invalid": "Некорректный URL", + "display_title": "Отображаемое название", + "link_title_placeholder": "Как вы хотите видеть эту ссылку", + "url": "URL", + "side_peek": "Боковой просмотр", + "modal": "Модальное окно", + "full_screen": "Полный экран", + "close_peek_view": "Закрыть просмотр", + "toggle_peek_view_layout": "Переключить макет просмотра", + "options": "Опции", + "duration": "Продолжительность", + "today": "Сегодня", + "week": "Неделя", + "month": "Месяц", + "quarter": "Квартал", + "press_for_commands": "Нажмите '/' для команд", + "click_to_add_description": "Нажмите, чтобы добавить описание", + "search": { + "label": "Поиск", + "placeholder": "Введите для поиска", + "no_matches_found": "Совпадений не найдено", + "no_matching_results": "Нет подходящих результатов" + }, + "actions": { + "edit": "Редактировать", + "make_a_copy": "Сделать копию", + "open_in_new_tab": "Открыть в новой вкладке", + "copy_link": "Копировать ссылку", + "archive": "Архивировать", + "restore": "Восстановить", + "delete": "Удалить", + "remove_relation": "Удалить связь", + "subscribe": "Подписаться", + "unsubscribe": "Отписаться", + "clear_sorting": "Сбросить сортировку", + "show_weekends": "Показывать выходные", + "enable": "Включить", + "disable": "Отключить" + }, + "name": "Название", + "discard": "Отменить", + "confirm": "Подтвердить", + "confirming": "Подтверждение", + "read_the_docs": "Документация", + "default": "По умолчанию", + "active": "Активный", + "enabled": "Включён", + "disabled": "Отключён", + "mandate": "Мандат", + "mandatory": "Обязательный", + "yes": "Да", + "no": "Нет", + "please_wait": "Пожалуйста, подождите", + "enabling": "Включение", + "disabling": "Отключение", + "beta": "Бета", + "or": "или", + "next": "Далее", + "back": "Назад", + "cancelling": "Отмена", + "configuring": "Настройка", + "clear": "Очистить", + "import": "Импорт", + "connect": "Подключить", + "authorizing": "Авторизация", + "processing": "Обработка", + "no_data_available": "Нет доступных данных", + "from": "от {name}", + "authenticated": "Авторизован", + "select": "Выбрать", + "upgrade": "Обновить", + "add_seats": "Добавить места", + "projects": "Проекты", + "workspace": "Рабочее пространство", + "workspaces": "Рабочие пространства", + "team": "Команда", + "teams": "Команды", + "entity": "Сущность", + "entities": "Сущности", + "task": "Рабочий элемент", + "tasks": "Рабочие элементы", + "section": "Секция", + "sections": "Секции", + "edit": "Редактировать", + "connecting": "Подключение", + "connected": "Подключён", + "disconnect": "Отключить", + "disconnecting": "Отключение", + "installing": "Установка", + "install": "Установить", + "reset": "Сбросить", + "live": "В прямом эфире", + "change_history": "История изменений", + "coming_soon": "Скоро", + "members": "Участники", + "you": "Вы", + "upgrade_cta": { + "higher_subscription": "Перейти на подписку выше", + "talk_to_sales": "Связаться с отделом продаж" + }, + "category": "Категория", + "categories": "Категории", + "saving": "Сохранение", + "save_changes": "Сохранить изменения", + "delete": "Удалить", + "deleting": "Удаление", + "pending": "Ожидание", + "invite": "Пригласить" + }, + + "chart": { + "x_axis": "Ось X", + "y_axis": "Ось Y", + "metric": "Метрика" + }, + + "form": { + "title": { + "required": "Название обязательно", + "max_length": "Название должно быть короче {length} символов" + } + }, + + "entity": { + "grouping_title": "Группировка {entity}", + "priority": "Приоритет {entity}", + "all": "Все {entity}", + "drop_here_to_move": "Переместите {entity} сюда", + "delete": { + "label": "Удалить {entity}", + "success": "{entity} успешно удалён", + "failed": "Ошибка удаления {entity}" + }, + "update": { + "failed": "Ошибка обновления {entity}", + "success": "{entity} успешно обновлён" + }, + "link_copied_to_clipboard": "Ссылка на {entity} скопирована", + "fetch": { + "failed": "Ошибка получения {entity}" + }, + "add": { + "success": "{entity} успешно добавлен", + "failed": "Ошибка добавления {entity}" + } + }, + + "epic": { + "all": "Все эпики", + "label": "{count, plural, one {Эпик} other {Эпики}}", + "new": "Новый эпик", + "adding": "Добавление эпика", + "create": { + "success": "Эпик успешно создан" + }, + "add": { + "press_enter": "Нажмите 'Enter' чтобы добавить ещё эпик", + "label": "Добавить эпик" + }, + "title": { + "label": "Название эпика", + "required": "Название эпика обязательно" + } + }, + + "issue": { + "label": "{count, plural, one {Рабочий элемент} other {Рабочие элементы}}", + "all": "Все рабочие элементы", + "edit": "Редактировать рабочий элемент", + "title": { + "label": "Название рабочего элемента", + "required": "Название рабочего элемента обязательно." + }, + "add": { + "press_enter": "Нажмите 'Enter' чтобы добавить ещё рабочий элемент", + "label": "Добавить рабочий элемент", + "cycle": { + "failed": "Не удалось добавить рабочий элемент в цикл. Попробуйте снова.", + "success": "{count, plural, one {Рабочий элемент} other {Рабочие элементы}} успешно {count, plural, one {добавлен} other {добавлены}} в цикл.", + "loading": "Добавление {count, plural, one {рабочего элемента} other {рабочих элементов}} в цикл" + }, + "assignee": "Добавить ответственных", + "start_date": "Добавить дату начала", + "due_date": "Добавить срок выполнения", + "parent": "Добавить родительский рабочий элемент", + "sub_issue": "Добавить подэлемент", + "relation": "Добавить связь", + "link": "Добавить ссылку", + "existing": "Добавить существующий рабочий элемент" + }, + "remove": { + "label": "Удалить рабочий элемент", + "cycle": { + "loading": "Удаление рабочего элемента из цикла", + "success": "Рабочий элемент успешно удален из цикла", + "failed": "Не удалось удалить рабочий элемент из цикла. Попробуйте снова." + }, + "module": { + "loading": "Удаление рабочего элемента из модуля", + "success": "Рабочий элемент успешно удален из модуля.", + "failed": "Не удалось удалить рабочий элемент из модуля. Попробуйте снова." + }, + "parent": { + "label": "Удалить родительский рабочий элемент" + } + }, + "new": "Новый рабочий элемент", + "adding": "Добавление рабочего элемента", + "create": { + "success": "Рабочий элемент успешно создан" + }, + "priority": { + "urgent": "Срочный", + "high": "Высокий", + "medium": "Средний", + "low": "Низкий" + }, + "display": { + "properties": { + "label": "Отображаемые свойства", + "id": "ID", + "issue_type": "Тип рабочего элемента", + "sub_issue_count": "Количество подэлементов", + "attachment_count": "Количество вложений", + "created_on": "Дата создания", + "sub_issue": "Подэлемент" + }, + "extra": { + "show_sub_issues": "Показывать подэлементы", + "show_empty_groups": "Показывать пустые группы" + } + }, + "layouts": { + "ordered_by_label": "Сортировка по", + "list": "Список", + "kanban": "Доска", + "calendar": "Календарь", + "spreadsheet": "Таблица", + "gantt": "График", + "title": { + "list": "Список", + "kanban": "Доска", + "calendar": "Календарь", + "spreadsheet": "Таблица", + "gantt": "График" + } + }, + "states": { + "active": "Активно", + "backlog": "Бэклог" + }, + "comments": { + "placeholder": "Добавить комментарий", + "switch": { + "private": "Изменить на приватный комментарий", + "public": "Изменить на публичный комментарий" + }, + "create": { + "success": "Комментарий успешно добавлен", + "error": "Ошибка создания комментария. Попробуйте позже." + }, + "update": { + "success": "Комментарий успешно обновлён", + "error": "Ошибка обновления комментария. Попробуйте позже." + }, + "remove": { + "success": "Комментарий успешно удалён", + "error": "Ошибка удаления комментария. Попробуйте позже." + }, + "upload": { + "error": "Ошибка загрузки файла. Попробуйте позже." + } + }, + "empty_state": { + "issue_detail": { + "title": "Рабочий элемент не существует", + "description": "Данный рабочий элемент был удален, архивирован или не существует.", + "primary_button": { + "text": "Посмотреть другие рабочие элементы" + } + } + }, + "sibling": { + "label": "Связанные рабочие элементы" + }, + "archive": { + "description": "Только завершённые или отменённые\nрабочие элементы можно архивировать", + "label": "Архивировать рабочий элемент", + "confirm_message": "Вы уверены что хотите архивировать рабочий элемент? Архивы можно восстановить позже.", + "success": { + "label": "Архивация успешна", + "message": "Архивы доступны в разделе архивов проекта" + }, + "failed": { + "message": "Ошибка архивации рабочего элемента. Попробуйте позже." + } + }, + "restore": { + "success": { + "title": "Восстановление успешно", + "message": "Рабочий элемент доступен в разделе рабочих элементов проекта" + }, + "failed": { + "message": "Ошибка восстановления рабочего элемента. Попробуйте позже." + } + }, + "relation": { + "relates_to": "Связан с", + "duplicate": "Дубликат", + "blocked_by": "Блокируется", + "blocking": "Блокирует" + }, + "copy_link": "Копировать ссылку на рабочий элемент", + "delete": { + "label": "Удалить рабочий элемент", + "error": "Ошибка удаления рабочего элемента" + }, + "subscription": { + "actions": { + "subscribed": "Подписка на рабочий элемент оформлена", + "unsubscribed": "Подписка на рабочий элемент отменена" + } + }, + "select": { + "error": "Выберите хотя бы один рабочий элемент", + "empty": "Рабочие элементы не выбраны", + "add_selected": "Добавить выбранные рабочие элементы" + }, + "open_in_full_screen": "Открыть рабочий элемент в полном экране" + }, + + "attachment": { + "error": "Ошибка прикрепления файла", + "only_one_file_allowed": "Можно загрузить только один файл", + "file_size_limit": "Максимальный размер файла - {size} МБ", + "drag_and_drop": "Перетащите файл для загрузки", + "delete": "Удалить вложение" + }, + + "label": { + "select": "Выбрать метку", + "create": { + "success": "Метка создана", + "failed": "Ошибка создания метки", + "already_exists": "Метка уже существует", + "type": "Введите новую метку" + } + }, + + "sub_work_item": { + "update": { + "success": "Подэлемент успешно обновлен", + "error": "Ошибка обновления подэлемента" + }, + "remove": { + "success": "Подэлемент успешно удален", + "error": "Ошибка удаления подэлемента" + } + }, + + "view": { + "label": "{count, plural, one {Представление} other {Представления}}", + "create": { + "label": "Создать представление" + }, + "update": { + "label": "Обновить представление" + } + }, + + "inbox_issue": { + "status": { + "pending": { + "title": "Ожидание", + "description": "Ожидание" + }, + "declined": { + "title": "Отклонено", + "description": "Отклонено" + }, + "snoozed": { + "title": "Отложено", + "description": "{days, plural, one{# день} other{# дней}} осталось" + }, + "accepted": { + "title": "Принято", + "description": "Принято" + }, + "duplicate": { + "title": "Дубликат", + "description": "Дубликат" + } + }, + "modals": { + "decline": { + "title": "Отклонить рабочий элемент", + "content": "Вы уверены, что хотите отклонить рабочий элемент {value}?" + }, + "delete": { + "title": "Удалить рабочий элемент", + "content": "Вы уверены, что хотите удалить рабочий элемент {value}?", + "success": "Рабочий элемент успешно удален" + } + }, + "errors": { + "snooze_permission": "Только администраторы проекта могут откладывать/возобновлять рабочие элементы", + "accept_permission": "Только администраторы проекта могут принимать рабочие элементы", + "decline_permission": "Только администраторы проекта могут отклонять рабочие элементы" + }, + "actions": { + "accept": "Принять", + "decline": "Отклонить", + "snooze": "Отложить", + "unsnooze": "Возобновить", + "copy": "Копировать ссылку на рабочий элемент", + "delete": "Удалить", + "open": "Открыть рабочий элемент", + "mark_as_duplicate": "Пометить как дубликат", + "move": "Перенести {value} в рабочие элементы проекта" + }, + "source": { + "in-app": "в приложении" + }, + "order_by": { + "created_at": "Дата создания", + "updated_at": "Дата обновления", + "id": "ID" + }, + "label": "Входящие", + "page_label": "{workspace} - Входящие", + "modal": { + "title": "Создать входящий рабочий элемент" + }, + "tabs": { + "open": "Открытые", + "closed": "Закрытые" + }, + "empty_state": { + "sidebar_open_tab": { + "title": "Нет открытых рабочих элементов", + "description": "Здесь отображаются открытые рабочие элементы. Создайте новый рабочий элемент." + }, + "sidebar_closed_tab": { + "title": "Нет закрытых рабочих элементов", + "description": "Все рабочие элементы, принятые или отклоненные, можно найти здесь." + }, + "sidebar_filter": { + "title": "Нет подходящих рабочих элементов", + "description": "Не найдено рабочих элементов по примененным фильтрам. Создайте новый рабочий элемент." + }, + "detail": { + "title": "Выберите рабочий элемент для просмотра деталей." + } + } + }, + + "workspace_creation": { + "heading": "Создайте рабочее пространство", + "subheading": "Чтобы начать использовать Plane, создайте или присоединитесь к рабочему пространству.", + "form": { + "name": { + "label": "Название рабочего пространства", + "placeholder": "Лучше использовать знакомое и узнаваемое название" + }, + "url": { + "label": "Установите URL рабочего пространства", + "placeholder": "Введите или вставьте URL", + "edit_slug": "Можно редактировать только часть URL (slug)" + }, + "organization_size": { + "label": "Сколько человек будут использовать это пространство?", + "placeholder": "Выберите диапазон" + } + }, + "errors": { + "creation_disabled": { + "title": "Только администратор экземпляра может создавать рабочие пространства", + "description": "Если вы знаете email администратора, нажмите кнопку ниже для связи.", + "request_button": "Запросить администратора" + }, + "validation": { + "name_alphanumeric": "Название может содержать только пробелы, дефисы, подчёркивания и буквенно-цифровые символы", + "name_length": "Максимальная длина названия - 80 символов", + "url_alphanumeric": "URL может содержать только дефисы и буквенно-цифровые символы", + "url_length": "Максимальная длина URL - 48 символов", + "url_already_taken": "Этот URL уже занят!" + } + }, + "request_email": { + "subject": "Запрос нового рабочего пространства", + "body": "Здравствуйте, администратор!\n\nПожалуйста, создайте новое рабочее пространство с URL [/workspace-name] для [цель создания].\n\nСпасибо,\n{firstName} {lastName}\n{email}" + }, + "button": { + "default": "Создать пространство", + "loading": "Создание пространства" + }, + "toast": { + "success": { + "title": "Успех", + "message": "Рабочее пространство успешно создано" + }, + "error": { + "title": "Ошибка", + "message": "Не удалось создать пространство. Попробуйте снова." + } + } + }, + + "workspace_dashboard": { + "empty_state": { + "general": { + "title": "Обзор проектов, активности и метрик", + "description": "Добро пожаловать в Plane! Создайте первый проект и отслеживайте рабочие элементы - эта страница станет вашим рабочим пространством. Администраторы также увидят элементы для управления командой.", + "primary_button": { + "text": "Создать первый проект", + "comic": { + "title": "Всё начинается с проекта в Plane", + "description": "Проектом может быть роадмап продукта, маркетинговая кампания или запуск нового автомобиля." + } + } + } + } + }, + + "workspace_analytics": { + "label": "Аналитика", + "page_label": "{workspace} - Аналитика", + "open_tasks": "Всего открытых рабочих элементов", + "error": "Ошибка при получении данных", + "work_items_closed_in": "Рабочие элементы закрыты в", + "selected_projects": "Выбранные проекты", + "total_members": "Всего участников", + "total_cycles": "Всего циклов", + "total_modules": "Всего модулей", + "pending_work_items": { + "title": "Ожидающие рабочие элементы", + "empty_state": "Здесь отображается анализ ожидающих рабочих элементов по сотрудникам." + }, + "work_items_closed_in_a_year": { + "title": "Рабочие элементы закрытые за год", + "empty_state": "Закрывайте рабочие элементы для просмотра анализа в виде графика." + }, + "most_work_items_created": { + "title": "Наибольшее количество созданных рабочих элементов", + "empty_state": "Здесь отображаются сотрудники и количество созданных ими рабочих элементов." + }, + "most_work_items_closed": { + "title": "Наибольшее количество закрытых рабочих элементов", + "empty_state": "Здесь отображаются сотрудники и количество закрытых ими рабочих элементов." + }, + "tabs": { + "scope_and_demand": "Объём и спрос", + "custom": "Пользовательская аналитика" + }, + "empty_state": { + "general": { + "title": "Отслеживайте прогресс, загрузку и распределение ресурсов", + "description": "Анализируйте объёмы работ, оценивайте сроки и контролируйте выполнение проектов. Отслеживайте производительность команды и соблюдайте сроки.", + "primary_button": { + "text": "Начать первый проект", + "comic": { + "title": "Аналитика лучше всего работает с Циклами + Модулями", + "description": "Сначала группируйте рабочие элементы в Циклы, а при возможности - объединяйте рабочие элементы в Модули. Найдите оба раздела в левом меню." + } + } + } + } + }, + + "workspace_projects": { + "label": "{count, plural, one {Проект} other {Проекты}}", + "create": { + "label": "Добавить проект" + }, + "network": { + "label": "Сеть", + "private": { + "title": "Приватный", + "description": "Доступ только по приглашению" + }, + "public": { + "title": "Публичный", + "description": "Доступен всем в рабочем пространстве кроме гостей" + } + }, + "error": { + "permission": "Недостаточно прав для выполнения действия", + "cycle_delete": "Ошибка удаления цикла", + "module_delete": "Ошибка удаления модуля", + "issue_delete": "Не удалось удалить рабочий элемент" + }, + "state": { + "backlog": "Бэклог", + "unstarted": "Не начато", + "started": "В процессе", + "completed": "Завершено", + "cancelled": "Отменено" + }, + "sort": { + "manual": "Вручную", + "name": "По имени", + "created_at": "По дате создания", + "members_length": "По количеству участников" + }, + "scope": { + "my_projects": "Мои проекты", + "archived_projects": "Архивные" + }, + "common": { + "months_count": "{months, plural, one{# месяц} other{# месяцев}}" + }, + "empty_state": { + "general": { + "title": "Нет активных проектов", + "description": "Проекты помогают организовать работу. Создавайте проекты для управления рабочими элементами, циклами и модулями. Фильтруйте архивные проекты при необходимости.", + "primary_button": { + "text": "Создать первый проект", + "comic": { + "title": "Всё начинается с проекта в Plane", + "description": "Проектом может быть дорожная карта продукта, маркетинговая кампания или запуск нового автомобиля." + } + } + }, + "no_projects": { + "title": "Нет проектов", + "description": "Для управления рабочими элементами необходимо создать проект или быть его участником.", + "primary_button": { + "text": "Создать первый проект", + "comic": { + "title": "Всё начинается с проекта в Plane", + "description": "Проектом может быть дорожная карта продукта, маркетинговая кампания или запуск нового автомобиля." + } + } + }, + "filter": { + "title": "Нет подходящих проектов", + "description": "Не найдено проектов по заданным критериям.\nСоздайте новый проект." + }, + "search": { + "description": "Не найдено проектов по заданным критериям.\nСоздайте новый проект" + } + } + }, + + "workspace_views": { + "add_view": "Добавить представление", + "empty_state": { + "all-issues": { + "title": "Нет рабочих элементов в проекте", + "description": "Первый проект создан! Теперь разделите работу на отслеживаемые рабочие элементы.", + "primary_button": { + "text": "Создать рабочий элемент" + } + }, + "assigned": { + "title": "Нет назначенных рабочих элементов", + "description": "Здесь отображаются рабочие элементы, назначенные вам.", + "primary_button": { + "text": "Создать рабочий элемент" + } + }, + "created": { + "title": "Нет созданных рабочих элементов", + "description": "Все созданные вами рабочие элементы отображаются здесь.", + "primary_button": { + "text": "Создать рабочий элемент" + } + }, + "subscribed": { + "title": "Нет отслеживаемых рабочих элементов", + "description": "Подпишитесь на интересующие рабочие элементы для отслеживания." + }, + "custom-view": { + "title": "Нет рабочих элементов", + "description": "Здесь отображаются рабочие элементы, соответствующие фильтрам." + } + } + }, + + "workspace_settings": { + "label": "Настройки пространства", + "page_label": "{workspace} - Основные настройки", + "key_created": "Ключ создан", + "copy_key": "Скопируйте и сохраните секретный ключ в Plane Pages. После закрытия ключ будет недоступен. CSV-файл с ключом был скачан.", + "token_copied": "Токен скопирован в буфер", + "settings": { + "general": { + "title": "Основные", + "upload_logo": "Загрузить логотип", + "edit_logo": "Изменить логотип", + "name": "Название пространства", + "company_size": "Размер компании", + "url": "URL пространства", + "update_workspace": "Обновить пространство", + "delete_workspace": "Удалить пространство", + "delete_workspace_description": "Все данные будут безвозвратно удалены.", + "delete_btn": "Удалить пространство", + "delete_modal": { + "title": "Подтвердите удаление пространства", + "description": "У вас есть активная пробная подписка. Сначала отмените её.", + "dismiss": "Отмена", + "cancel": "Отменить подписку", + "success_title": "Пространство удалено", + "success_message": "Вы будете перенаправлены в профиль", + "error_title": "Ошибка", + "error_message": "Попробуйте снова" + }, + "errors": { + "name": { + "required": "Обязательное поле", + "max_length": "Максимум 80 символов" + }, + "company_size": { + "required": "Размер компании обязателен" + } + } + }, + "members": { + "title": "Участники", + "add_member": "Добавить участника", + "pending_invites": "Ожидающие приглашения", + "invitations_sent_successfully": "Приглашения отправлены", + "leave_confirmation": "Подтвердите выход из пространства. Доступ будет утрачен. Это действие нельзя отменить.", + "details": { + "full_name": "Полное имя", + "display_name": "Отображаемое имя", + "email_address": "Email", + "account_type": "Тип аккаунта", + "authentication": "Аутентификация", + "joining_date": "Дата присоединения" + }, + "modal": { + "title": "Пригласить участников", + "description": "Пригласите коллег в рабочее пространство.", + "button": "Отправить приглашения", + "button_loading": "Отправка...", + "placeholder": "name@company.com", + "errors": { + "required": "Введите email адрес, чтобы пригласить участников", + "invalid": "Неверный email" + } + } + }, + "billing_and_plans": { + "title": "Оплата и тарифы", + "current_plan": "Текущий тариф", + "free_plan": "Используется бесплатный тариф", + "view_plans": "Посмотреть тарифы" + }, + "exports": { + "title": "Экспорт", + "exporting": "Экспортируется", + "previous_exports": "Предыдущие экспорты", + "export_separate_files": "Экспорт в отдельные файлы", + "modal": { + "title": "Экспорт в", + "toasts": { + "success": { + "title": "Успешный экспорт", + "message": "Экспортированные {entity} доступны для скачивания." + }, + "error": { + "title": "Ошибка экспорта", + "message": "Эскпорт не удался. Попробуйте снова" + } + } + } + }, + "webhooks": { + "title": "Вебхуки", + "add_webhook": "Добавить вебхук", + "modal": { + "title": "Создать вебхук", + "details": "Детали вебхука", + "payload": "URL для уведомлений", + "question": "Какие события будут активировать вебхук?", + "error": "Требуется URL" + }, + "secret_key": { + "title": "Секретный ключ", + "message": "Сгенерируйте токен для подписи уведомлений" + }, + "options": { + "all": "Все события", + "individual": "Выбрать события" + }, + "toasts": { + "created": { + "title": "Вебхук создан", + "message": "Вебхук успешно создан" + }, + "not_created": { + "title": "Ошибка создания", + "message": "Не удалось создать вебхук" + }, + "updated": { + "title": "Обновлено", + "message": "Вебхук успешно обновлён" + }, + "not_updated": { + "title": "Ошибка обновления", + "message": "Не удалось обновить вебхук" + }, + "removed": { + "title": "Вебхук удалён", + "message": "Вебхук успешно удалён" + }, + "not_removed": { + "title": "Ошибка удаления вебхука", + "message": "Не удалось удалить вебхук" + }, + "secret_key_copied": { + "message": "Секретный ключ скопирован" + }, + "secret_key_not_copied": { + "message": "Ошибка копирования ключа" + } + } + }, + "api_tokens": { + "title": "API-токены", + "add_token": "Добавить токен", + "create_token": "Создать токен", + "never_expires": "Бессрочный", + "generate_token": "Сгенерировать токен", + "generating": "Генерация", + "delete": { + "title": "Удалить токен", + "description": "Приложения, использующие этот токен, потеряют доступ. Действие необратимо.", + "success": { + "title": "Успех!", + "message": "Токен удалён" + }, + "error": { + "title": "Ошибка!", + "message": "Не удалось удалить токен" + } + } + } + }, + "empty_state": { + "api_tokens": { + "title": "Нет API-токенов", + "description": "Используйте API Plane для интеграции с внешними системами. Создайте тоекен чтобы начать." + }, + "webhooks": { + "title": "Нет вебхуков", + "description": "Создавайте вебхуки для автоматизации и получения уведомлений." + }, + "exports": { + "title": "Нет экспортов", + "description": "Здесь будут сохранённые копии ваших экспортов." + }, + "imports": { + "title": "Нет импортов", + "description": "Здесь отображаются все предыдущие импорты." + } + } + }, + + "profile": { + "label": "Профиль", + "page_label": "Ваша работа", + "work": "Работа", + "details": { + "joined_on": "Присоединился", + "time_zone": "Часовой пояс" + }, + "stats": { + "workload": "Нагрузка", + "overview": "Обзор", + "created": "Созданные рабочие элементы", + "assigned": "Назначенные рабочие элементы", + "subscribed": "Отслеживаемые рабочие элементы", + "state_distribution": { + "title": "Рабочие элементы по статусам", + "empty": "Создавайте рабочие элементы для анализа по статусам" + }, + "priority_distribution": { + "title": "Рабочие элементы по приоритетам", + "empty": "Создавайте рабочие элементы для анализа по приоритетам" + }, + "recent_activity": { + "title": "Недавняя активность", + "empty": "Данные не найдены", + "button": "Скачать активность", + "button_loading": "Скачивание" + } + }, + "actions": { + "profile": "Профиль", + "security": "Безопасность", + "activity": "Активность", + "appearance": "Внешний вид", + "notifications": "Уведомления" + }, + "tabs": { + "summary": "Сводка", + "assigned": "Назначенные", + "created": "Созданные", + "subscribed": "Отслеживаемые", + "activity": "Активность" + }, + "empty_state": { + "activity": { + "title": "Нет активности", + "description": "Создайте первый рабочий элемент для начала работы! Добавьте детали и свойства рабочего элемента. Исследуйте больше в Plane, чтобы увидеть вашу активность." + }, + "assigned": { + "title": "Нет назначенных рабочих элементов", + "description": "Здесь отображаются рабочие элементы, назначенные вам." + }, + "created": { + "title": "Нет созданных рабочих элементов", + "description": "Все созданные вами рабочие элементы отображаются здесь." + }, + "subscribed": { + "title": "Нет отслеживаемых рабочих элементов", + "description": "Подпишитесь на интересующие рабочие элементы." + } + } + }, + + "project_settings": { + "general": { + "enter_project_id": "Введите ID проекта", + "please_select_a_timezone": "Выберите часовой пояс", + "archive_project": { + "title": "Архивировать проект", + "description": "Проект исчезнет из бокового меню, но останется доступным на странице проектов. Можно восстановить или удалить позже.", + "button": "Архивировать" + }, + "delete_project": { + "title": "Удалить проект", + "description": "Все данные проекта будут безвозвратно удалены без возможности восстановления.", + "button": "Удалить проект" + }, + "toast": { + "success": "Проект обновлён", + "error": "Ошибка обновления. Попробуйте снова." + } + }, + "members": { + "label": "Участники", + "project_lead": "Руководитель проекта", + "default_assignee": "Ответственный по умолчанию", + "guest_super_permissions": { + "title": "Дать гостям доступ на просмотр всех рабочих элементов:", + "sub_heading": "Гости смогут просматривать все рабочие элементы проекта" + }, + "invite_members": { + "title": "Пригласить участников", + "sub_heading": "Пригласите коллег для работы над проектом.", + "select_co_worker": "Выберите сотрудника" + } + }, + "states": { + "describe_this_state_for_your_members": "Опишите этот статус для участников", + "empty_state": { + "title": "Нет статусов для группы {groupKey}", + "description": "Создайте новый статус" + } + }, + "labels": { + "label_title": "Название метки", + "label_title_is_required": "Название обязательно", + "label_max_char": "Максимальная длина названия - 255 символов", + "toast": { + "error": "Ошибка обновления метки" + } + }, + "estimates": { + "title": "Включить оценку для проекта", + "description": "Помогают оценивать сложность и загрузку команды." + }, + "automations": { + "label": "Автоматизация", + "auto-archive": { + "title": "Автоархивация закрытых рабочих элементов", + "description": "Plane будет автоматически архивировать завершённые или отменённые рабочие элементы.", + "duration": "Архивировать рабочие элементы закрытые более" + }, + "auto-close": { + "title": "Автозакрытие рабочих элементов", + "description": "Plane будет автоматически закрывать неактивные рабочие элементы.", + "duration": "Закрывать рабочие элементы неактивные более", + "auto_close_status": "Статус автозакрытия" + } + }, + + "empty_state": { + "labels": { + "title": "Нет меток", + "description": "Создавайте метки для организации и фильтрации рабочих элементов." + }, + "estimates": { + "title": "Нет систем оценки", + "description": "Создайте систему оценок для планирования работ.", + "primary_button": "Добавить систему" + } + } + }, + + "project_cycles": { + "add_cycle": "Добавить цикл", + "more_details": "Подробнее", + "cycle": "Цикл", + "update_cycle": "Обновить цикл", + "create_cycle": "Создать цикл", + "no_matching_cycles": "Нет подходящих циклов", + "remove_filters_to_see_all_cycles": "Снимите фильтры для просмотра всех циклов", + "remove_search_criteria_to_see_all_cycles": "Очистите поиск для просмотра всех циклов", + "only_completed_cycles_can_be_archived": "Только завершённые циклы можно архивировать", + "active_cycle": { + "label": "Активный цикл", + "progress": "Прогресс", + "chart": "Диаграмма сгорания", + "priority_issue": "Приоритетные рабочие элементы", + "assignees": "Ответственные", + "issue_burndown": "Выгорание рабочих элементов", + "ideal": "Идеальный", + "current": "Текущий", + "labels": "Метки" + }, + "upcoming_cycle": { + "label": "Предстоящий цикл" + }, + "completed_cycle": { + "label": "Завершённый цикл" + }, + "status": { + "days_left": "Дней осталось", + "completed": "Завершено", + "yet_to_start": "Ещё не начато", + "in_progress": "В процессе", + "draft": "Черновик" + }, + "action": { + "restore": { + "title": "Восстановить цикл", + "success": { + "title": "Цикл восстановлен", + "description": "Цикл успешно восстановлен" + }, + "failed": { + "title": "Ошибка восстановления", + "description": "Не удалось восстановить цикл" + } + }, + "favorite": { + "loading": "Добавление в избранное", + "success": { + "description": "Цикл добавлен в избранное", + "title": "Успех!" + }, + "failed": { + "description": "Ошибка добавления в избранное", + "title": "Ошибка!" + } + }, + "unfavorite": { + "loading": "Удаление из избранного", + "success": { + "description": "Цикл удалён из избранного", + "title": "Успех!" + }, + "failed": { + "description": "Ошибка удаления цикла из избранного. Попробуйте снова.", + "title": "Ошибка!" + } + }, + "update": { + "loading": "Обновление цикла", + "success": { + "description": "Цикл успешно обновлён", + "title": "Успех!" + }, + "failed": { + "description": "Ошибка обновления цикла. Попробуйте снова.", + "title": "Ошибка!" + }, + "error": { + "already_exists": "Цикл на указанные даты уже существует. Для создания черновика удалите даты." + } + } + }, + "empty_state": { + "general": { + "title": "Организуйте работу в циклах", + "description": "Разбивайте работу на временные интервалы, устанавливайте сроки и отслеживайте прогресс команды.", + "primary_button": { + "text": "Создать первый цикл", + "comic": { + "title": "Циклы - повторяющиеся временные интервалы", + "description": "Спринт, итерация или любой другой термин для еженедельного/двухнедельного планирования." + } + } + }, + "no_issues": { + "title": "Нет рабочих элементов в цикле", + "description": "Добавьте существующие или создайте новые рабочие элементы для этого цикла", + "primary_button": { + "text": "Создать рабочий элемент" + }, + "secondary_button": { + "text": "Добавить существующий рабочий элемент" + } + }, + "completed_no_issues": { + "title": "Нет рабочих элементов в цикле", + "description": "Нет рабочих элементов. Рабочие элементы были перенесены или скрыты. Для просмотра измените настройки отображения." + }, + "active": { + "title": "Нет активных циклов", + "description": "Активный цикл включает текущую дату. Здесь отображается прогресс активного цикла." + }, + "archived": { + "title": "Нет архивных циклов", + "description": "Архивируйте завершённые циклы для упорядочивания проекта." + } + } + }, + + "project_issues": { + "empty_state": { + "no_issues": { + "title": "Создайте рабочий элемент и назначьте исполнителя", + "description": "Рабочие элементы помогают организовать работу команды. Создавайте, назначайте и завершайте рабочие элементы для достижения целей проекта.", + "primary_button": { + "text": "Создать первый рабочий элемент", + "comic": { + "title": "Рабочие элементы - строительные блоки Plane", + "description": "Примеры рабочих элементов: редизайн интерфейса, ребрендинг компании или запуск новой системы." + } + } + }, + "no_archived_issues": { + "title": "Нет архивных рабочих элементов", + "description": "Архивируйте завершённые или отменённые рабочие элементы вручную или автоматически.", + "primary_button": { + "text": "Настроить автоматизацию" + } + }, + "issues_empty_filter": { + "title": "Нет рабочих элементов подходящих фильтрам", + "secondary_button": { + "text": "Сбросить фильтры" + } + } + } + }, + + "project_module": { + "add_module": "Добавить модуль", + "update_module": "Обновить модуль", + "create_module": "Создать модуль", + "archive_module": "Архивировать модуль", + "restore_module": "Восстановить модуль", + "delete_module": "Удалить модуль", + "empty_state": { + "general": { + "title": "Связывайте этапы проекта с модулями для удобного отслеживания рабочих элементов.", + "description": "Модуль объединяет рабочие элементы по логическому или иерархическому признаку. Используйте модули для контроля этапов проекта. Каждый модуль имеет собственные сроки выполнения и аналитику для отслеживания прогресса.", + "primary_button": { + "text": "Создать первый модуль", + "comic": { + "title": "Модули группируют рабочие элементы по иерархии", + "description": "Примеры группировки: модуль корзины, модуль шасси или модуль склада." + } + } + }, + "no_issues": { + "title": "Нет рабочих элементов в модуле", + "description": "Создавайте или добавляйте рабочие элементы, которые хотите выполнить в рамках этого модуля", + "primary_button": { + "text": "Создать новые рабочие элементы" + }, + "secondary_button": { + "text": "Добавить существующий рабочий элемент" + } + }, + "archived": { + "title": "Нет архивных модулей", + "description": "Архивируйте завершённые или отменённые модули для упорядочивания проекта. Они появятся здесь после архивации." + }, + "sidebar": { + "in_active": "Этот модуль ещё не активен.", + "invalid_date": "Некорректная дата. Укажите правильную дату." + } + }, + "quick_actions": { + "archive_module": "Архивировать модуль", + "archive_module_description": "Только завершённые или отменённые\nмодули можно архивировать.", + "delete_module": "Удалить модуль" + }, + "toast": { + "copy": { + "success": "Ссылка на модуль скопирована в буфер обмена" + }, + "delete": { + "success": "Модуль успешно удалён", + "error": "Ошибка удаления модуля" + } + } + }, + + "project_views": { + "empty_state": { + "general": { + "title": "Сохраняйте фильтры в виде представлений. Создавайте неограниченное количество вариантов", + "description": "Представления - это сохранённые наборы фильтров для быстрого доступа. Все участники проекта видят созданные представления и могут выбирать подходящие.", + "primary_button": { + "text": "Создать первое представление", + "comic": { + "title": "Представления работают на основе свойств рабочих элементов", + "description": "Создавайте представления с любым количеством свойств в качестве фильтров." + } + } + }, + "filter": { + "title": "Подходящих представлений не найдено", + "description": "Нет представлений, соответствующих критериям поиска. \n Создайте новое представление." + } + } + }, + + "project_page": { + "empty_state": { + "general": { + "title": "Создавайте заметки, документы или базу знаний. Используйте Galileo, ИИ-помощник Plane.", + "description": "Страницы - пространство для организации мыслей в Plane. Делайте заметки, форматируйте текст, встраивайте рабочие элементы, используйте компоненты. Для быстрого создания документов используйте Galileo через горячие клавиши или кнопку.", + "primary_button": { + "text": "Создать первую страницу" + } + }, + "private": { + "title": "Нет приватных страниц", + "description": "Храните личные заметки здесь. Когда будете готовы поделиться, команда будет в одном клике.", + "primary_button": { + "text": "Создать первую страницу" + } + }, + "public": { + "title": "Нет публичных страниц", + "description": "Здесь отображаются страницы, доступные всем участникам проекта.", + "primary_button": { + "text": "Создать первую страницу" + } + }, + "archived": { + "title": "Нет архивных страниц", + "description": "Архивируйте неактуальные страницы. При необходимости вы найдете их здесь." + } + } + }, + + "command_k": { + "empty_state": { + "search": { + "title": "Ничего не найдено" + } + } + }, + + "issue_relation": { + "empty_state": { + "search": { + "title": "Не найдено подходящих рабочих элементов" + }, + "no_issues": { + "title": "Рабочие элементы не найдены" + } + } + }, + + "issue_comment": { + "empty_state": { + "general": { + "title": "Комментариев пока нет", + "description": "Используйте комментарии для обсуждения и отслеживания задач" + } + } + }, + + "notification": { + "label": "Входящие", + "page_label": "{workspace} - Входящие", + "options": { + "mark_all_as_read": "Пометить все как прочитанные", + "mark_read": "Пометить как прочитанное", + "mark_unread": "Пометить как непрочитанное", + "refresh": "Обновить", + "filters": "Фильтры входящих", + "show_unread": "Показать непрочитанные", + "show_snoozed": "Показать отложенные", + "show_archived": "Показать архивные", + "mark_archive": "Архивировать", + "mark_unarchive": "Разархивировать", + "mark_snooze": "Отложить", + "mark_unsnooze": "Возобновить" + }, + "toasts": { + "read": "Уведомление помечено как прочитанное", + "unread": "Уведомление помечено как непрочитанное", + "archived": "Уведомление архивировано", + "unarchived": "Уведомление разархивировано", + "snoozed": "Уведомление отложено", + "unsnoozed": "Уведомление возобновлено" + }, + "empty_state": { + "detail": { + "title": "Выберите для просмотра деталей" + }, + "all": { + "title": "Нет назначенных рабочих элементов", + "description": "Обновления по назначенным вам рабочим элементам будут \n отображаться здесь" + }, + "mentions": { + "title": "Нет упомянутых рабочих элементов", + "description": "Обновления по рабочим элементам, где вас упомянули, \n будут отображаться здесь" + } + }, + "tabs": { + "all": "Все", + "mentions": "Упоминания" + }, + "filter": { + "assigned": "Назначенные мне", + "created": "Созданные мной", + "subscribed": "Отслеживаемые мной" + }, + "snooze": { + "1_day": "1 день", + "3_days": "3 дня", + "5_days": "5 дней", + "1_week": "1 неделя", + "2_weeks": "2 недели", + "custom": "Другое" + } + }, + + "active_cycle": { + "empty_state": { + "progress": { + "title": "Добавьте рабочие элементы в цикл, чтобы отслеживать прогресс" + }, + "chart": { + "title": "Добавьте рабочие элементы в цикл для построения графика выполнения" + }, + "priority_issue": { + "title": "Просматривайте рабочие элементы с высоким приоритетом в цикле" + }, + "assignee": { + "title": "Назначьте ответственных, чтобы видеть распределение рабочих элементов" + }, + "label": { + "title": "Добавьте метки, чтобы видеть распределение рабочих элементов по категориям" + } + } + }, + + "disabled_project": { + "empty_state": { + "inbox": { + "title": "Функция 'Входящие' отключена для проекта", + "description": "Входящие помогают управлять запросами и добавлять их в рабочий процесс. Включите функцию в настройках проекта.", + "primary_button": { + "text": "Управление функциями" + } + }, + "cycle": { + "title": "Циклы отключены для этого проекта", + "description": "Разбивайте работу на временные интервалы, устанавливайте сроки и отслеживайте прогресс команды. Включите функцию циклов в настройках проекта.", + "primary_button": { + "text": "Управление функциями" + } + }, + "module": { + "title": "Модули отключены для проекта", + "description": "Модули - основные компоненты вашего проекта. Включите их в настройках проекта.", + "primary_button": { + "text": "Управление функциями" + } + }, + "page": { + "title": "Страницы отключены для проекта", + "description": "Страницы - основные компоненты вашего проекта. Включите их в настройках проекта.", + "primary_button": { + "text": "Управление функциями" + } + }, + "view": { + "title": "Представления отключены для проекта", + "description": "Представления - основные компоненты вашего проекта. Включите их в настройках проекта.", + "primary_button": { + "text": "Управление функциями" + } + } + } + }, + "workspace_draft_issues": { + "draft_an_issue": "Создать черновик рабочего элемента", + "empty_state": { + "title": "Черновики рабочих элементов, а вскоре и комментарии, будут отображаться здесь.", + "description": "Чтобы попробовать, начните добавлять рабочий элемент и прервитесь на полпути или создайте первый черновик ниже. 😉", + "primary_button": { + "text": "Создать первый черновик" + } + }, + "delete_modal": { + "title": "Удалить черновик", + "description": "Вы уверены, что хотите удалить этот черновик? Это действие нельзя отменить." + }, + "toasts": { + "created": { + "success": "Черновик создан", + "error": "Не удалось создать рабочий элемент. Попробуйте снова." + }, + "deleted": { + "success": "Черновик удалён" + } + } + }, + + "stickies": { + "title": "Ваши стикеры", + "placeholder": "нажмите, чтобы написать", + "all": "Все стикеры", + "no-data": "Запишите идею, зафиксируйте озарение или сохраните мысль. Создайте стикер, чтобы начать.", + "add": "Добавить стикер", + "search_placeholder": "Поиск по названию", + "delete": "Удалить стикер", + "delete_confirmation": "Вы уверены, что хотите удалить этот стикер?", + "empty_state": { + "simple": "Запишите идею, зафиксируйте озарение или сохраните мысль. Создайте стикер, чтобы начать.", + "general": { + "title": "Стикеры - это быстрые заметки и рабочие элементы, которые вы создаёте на лету.", + "description": "Легко фиксируйте свои мысли и идеи с помощью стикеров, которые доступны в любое время и в любом месте.", + "primary_button": { + "text": "Добавить стикер" + } + }, + "search": { + "title": "Ничего не найдено.", + "description": "Попробуйте другой запрос или сообщите нам,\nесли уверены в правильности поиска.", + "primary_button": { + "text": "Добавить стикер" + } + } + }, + "toasts": { + "errors": { + "wrong_name": "Название стикера не может быть длиннее 100 символов.", + "already_exists": "Стикер без описания уже существует" + }, + "created": { + "title": "Стикер создан", + "message": "Стикер успешно создан" + }, + "not_created": { + "title": "Ошибка создания стикера", + "message": "Не удалось создать стикер" + }, + "updated": { + "title": "Стикер обновлён", + "message": "Стикер успешно обновлён" + }, + "not_updated": { + "title": "Ошибка обновления", + "message": "Не удалось обновить стикер" + }, + "removed": { + "title": "Стикер удалён", + "message": "Стикер успешно удалён" + }, + "not_removed": { + "title": "Ошибка удаления", + "message": "Не удалось удалить стикер" + } + } + }, + + "role_details": { + "guest": { + "title": "Гость", + "description": "Внешние участники организаций могут быть приглашены как гости." + }, + "member": { + "title": "Участник", + "description": "Чтение, создание, редактирование и удаление элементов внутри проектов, циклов и модулей" + }, + "admin": { + "title": "Администратор", + "description": "Полные права доступа в рамках рабочего пространства." + } + }, + + "user_roles": { + "product_or_project_manager": "Продукт / Проект менеджер", + "development_or_engineering": "Разработка / Инжиниринг", + "founder_or_executive": "Основатель / Руководитель", + "freelancer_or_consultant": "Фрилансер / Консультант", + "marketing_or_growth": "Маркетинг / Рост", + "sales_or_business_development": "Продажи / Развитие бизнеса", + "support_or_operations": "Поддержка / Операции", + "student_or_professor": "Студент / Преподаватель", + "human_resources": "HR / Кадры", + "other": "Другое" + }, + + "importer": { + "github": { + "title": "GitHub", + "description": "Импорт рабочих элементов из репозиториев GitHub с синхронизацией." + }, + "jira": { + "title": "Jira", + "description": "Импорт рабочих элементов и эпиков из проектов Jira." + } + }, + + "exporter": { + "csv": { + "title": "CSV", + "description": "Экспорт рабочих элементов в CSV-файл.", + "short_description": "Экспорт в csv" + }, + "excel": { + "title": "Excel", + "description": "Экспорт рабочих элементов в файл Excel.", + "short_description": "Экспорт в excel" + }, + "xlsx": { + "title": "Excel", + "description": "Экспорт рабочих элементов в файл Excel.", + "short_description": "Экспорт в excel" + }, + "json": { + "title": "JSON", + "description": "Экспорт рабочих элементов в JSON-файл.", + "short_description": "Экспорт в json" + } + }, + "default_global_view": { + "all_issues": "Все рабочие элементы", + "assigned": "Назначенные", + "created": "Созданные", + "subscribed": "Подписанные" + }, + + "themes": { + "theme_options": { + "system_preference": { + "label": "Системные настройки" + }, + "light": { + "label": "Светлая" + }, + "dark": { + "label": "Тёмная" + }, + "light_contrast": { + "label": "Светлая высококонтрастностная" + }, + "dark_contrast": { + "label": "Тёмная высокая контрастность" + }, + "custom": { + "label": "Пользовательская тема" + } + } + }, + "project_modules": { + "status": { + "backlog": "Бэклог", + "planned": "Запланировано", + "in_progress": "В процессе", + "paused": "Приостановлено", + "completed": "Завершено", + "cancelled": "Отменено" + }, + "layout": { + "list": "Список", + "board": "Галерея", + "timeline": "Хронология" + }, + "order_by": { + "name": "Название", + "progress": "Прогресс", + "issues": "Количество рабочих элементов", + "due_date": "Срок выполнения", + "created_at": "Дата создания", + "manual": "Вручную" + } + }, + + "cycle": { + "label": "{count, plural, one {Цикл} other {Циклы}}", + "no_cycle": "Нет цикла" + }, + + "module": { + "label": "{count, plural, one {Модуль} other {Модули}}", + "no_module": "Нет модуля" + } +} diff --git a/packages/i18n/src/locales/zh-CN/translations.json b/packages/i18n/src/locales/zh-CN/translations.json index aca47f21a..14cf738f0 100644 --- a/packages/i18n/src/locales/zh-CN/translations.json +++ b/packages/i18n/src/locales/zh-CN/translations.json @@ -631,6 +631,8 @@ "states": "状态", "state": "状态", "state_groups": "状态组", + "state_group": "状态组", + "priorities": "优先级", "priority": "优先级", "team_project": "团队项目", "project": "项目", @@ -639,12 +641,16 @@ "module": "模块", "modules": "模块", "labels": "标签", + "label": "标签", "assignees": "负责人", "assignee": "负责人", "created_by": "创建者", "none": "无", "link": "链接", + "estimates": "估算", "estimate": "估算", + "created_at": "创建于", + "completed_at": "完成于", "layout": "布局", "filters": "筛选", "display": "显示", @@ -687,7 +693,6 @@ "add_more": "添加更多", "defaults": "默认值", "add_label": "添加标签", - "estimates": "估算", "customize_time_range": "自定义时间范围", "loading": "加载中", "attachments": "附件", @@ -825,8 +830,6 @@ "select": "选择", "upgrade": "升级", "add_seats": "添加席位", - "label": "标签", - "priorities": "优先级", "projects": "项目", "workspace": "工作区", "workspaces": "工作区", diff --git a/packages/i18n/src/store/index.ts b/packages/i18n/src/store/index.ts index 78e1311ad..4ee896ebb 100644 --- a/packages/i18n/src/store/index.ts +++ b/packages/i18n/src/store/index.ts @@ -147,6 +147,10 @@ export class TranslationStore { return import("../locales/ja/translations.json"); case "zh-CN": return import("../locales/zh-CN/translations.json"); + case "ru": + return import("../locales/ru/translations.json"); + case "it": + return import("../locales/it/translations.json"); default: throw new Error(`Unsupported language: ${language}`); } diff --git a/packages/i18n/src/types/language.ts b/packages/i18n/src/types/language.ts index 86e141ff5..ab984e260 100644 --- a/packages/i18n/src/types/language.ts +++ b/packages/i18n/src/types/language.ts @@ -1,4 +1,4 @@ -export type TLanguage = "en" | "fr" | "es" | "ja" | "zh-CN"; +export type TLanguage = "en" | "fr" | "es" | "ja" | "zh-CN" | "ru" | "it"; export interface ILanguageOption { label: string; diff --git a/packages/logger/package.json b/packages/logger/package.json index 5a2a06f9f..5eeacb65c 100644 --- a/packages/logger/package.json +++ b/packages/logger/package.json @@ -1,6 +1,7 @@ { "name": "@plane/logger", - "version": "0.25.0", + "version": "0.25.1", + "license": "AGPL-3.0", "description": "Logger shared across multiple apps internally", "private": true, "main": "./src/index.ts", diff --git a/packages/propel/package.json b/packages/propel/package.json index edc46d908..b82cb439b 100644 --- a/packages/propel/package.json +++ b/packages/propel/package.json @@ -1,7 +1,8 @@ { "name": "@plane/propel", - "version": "0.25.0", + "version": "0.25.1", "private": true, + "license": "AGPL-3.0", "scripts": { "lint": "eslint src --ext .ts,.tsx", "lint:errors": "eslint src --ext .ts,.tsx --quiet" diff --git a/packages/services/package.json b/packages/services/package.json index aad37ca44..2b2aae269 100644 --- a/packages/services/package.json +++ b/packages/services/package.json @@ -1,6 +1,7 @@ { "name": "@plane/services", - "version": "0.25.0", + "version": "0.25.1", + "license": "AGPL-3.0", "private": true, "main": "./src/index.ts", "scripts": { diff --git a/packages/shared-state/package.json b/packages/shared-state/package.json index 6a3a6a042..e3f73f142 100644 --- a/packages/shared-state/package.json +++ b/packages/shared-state/package.json @@ -1,6 +1,7 @@ { "name": "@plane/shared-state", - "version": "0.25.0", + "version": "0.25.1", + "license": "AGPL-3.0", "description": "Shared state shared across multiple apps internally", "private": true, "main": "./src/index.ts", diff --git a/packages/tailwind-config/package.json b/packages/tailwind-config/package.json index 30e6e7f7d..a1553e842 100644 --- a/packages/tailwind-config/package.json +++ b/packages/tailwind-config/package.json @@ -1,6 +1,7 @@ { "name": "@plane/tailwind-config", - "version": "0.25.0", + "version": "0.25.1", + "license": "AGPL-3.0", "description": "common tailwind configuration across monorepo", "main": "tailwind.config.js", "private": true, diff --git a/packages/types/package.json b/packages/types/package.json index 19eeea593..293e4f764 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,7 @@ { "name": "@plane/types", - "version": "0.25.0", + "version": "0.25.1", + "license": "AGPL-3.0", "private": true, "types": "./src/index.d.ts", "main": "./src/index.d.ts" diff --git a/packages/typescript-config/package.json b/packages/typescript-config/package.json index d0793e0ab..7d9f78958 100644 --- a/packages/typescript-config/package.json +++ b/packages/typescript-config/package.json @@ -1,6 +1,7 @@ { "name": "@plane/typescript-config", - "version": "0.25.0", + "version": "0.25.1", + "license": "AGPL-3.0", "private": true, "files": [ "base.json", diff --git a/packages/ui/package.json b/packages/ui/package.json index 0c66d0b44..d00920910 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -2,12 +2,12 @@ "name": "@plane/ui", "description": "UI components shared across multiple apps internally", "private": true, - "version": "0.25.0", + "version": "0.25.1", "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", "sideEffects": false, - "license": "MIT", + "license": "AGPL-3.0", "files": [ "dist/**" ], diff --git a/packages/utils/package.json b/packages/utils/package.json index be7411968..f340b5e8c 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,7 +1,8 @@ { "name": "@plane/utils", - "version": "0.25.0", + "version": "0.25.1", "description": "Helper functions shared across multiple apps internally", + "license": "AGPL-3.0", "private": true, "main": "./dist/index.js", "module": "./dist/index.mjs", diff --git a/space/package.json b/space/package.json index 50ed15a60..19ff0d6ff 100644 --- a/space/package.json +++ b/space/package.json @@ -1,7 +1,8 @@ { "name": "space", - "version": "0.25.0", + "version": "0.25.1", "private": true, + "license": "AGPL-3.0", "scripts": { "dev": "turbo run develop", "develop": "next dev -p 3002", diff --git a/web/app/[workspaceSlug]/(projects)/extended-project-sidebar.tsx b/web/app/[workspaceSlug]/(projects)/extended-project-sidebar.tsx index 3f3e21cfc..c79a63237 100644 --- a/web/app/[workspaceSlug]/(projects)/extended-project-sidebar.tsx +++ b/web/app/[workspaceSlug]/(projects)/extended-project-sidebar.tsx @@ -108,7 +108,7 @@ export const ExtendedProjectSidebar = observer(() => {
{
{ +export const StateOption = observer((props: TStateOptionProps) => { const { option, className = "" } = props; return ( diff --git a/web/core/components/cycles/analytics-sidebar/sidebar-header.tsx b/web/core/components/cycles/analytics-sidebar/sidebar-header.tsx index c8d4cc487..a41be6752 100644 --- a/web/core/components/cycles/analytics-sidebar/sidebar-header.tsx +++ b/web/core/components/cycles/analytics-sidebar/sidebar-header.tsx @@ -101,10 +101,10 @@ export const CycleSidebarHeader: FC = observer((props) => { }); }; - const submitChanges = (data: Partial, changedProperty: string) => { + const submitChanges = async (data: Partial, changedProperty: string) => { if (!workspaceSlug || !projectId || !cycleDetails.id) return; - updateCycleDetails(workspaceSlug.toString(), projectId.toString(), cycleDetails.id.toString(), data) + await updateCycleDetails(workspaceSlug.toString(), projectId.toString(), cycleDetails.id.toString(), data) .then((res) => { captureCycleEvent({ eventName: CYCLE_UPDATED, @@ -146,22 +146,21 @@ export const CycleSidebarHeader: FC = observer((props) => { }; const handleDateChange = async (startDate: Date | undefined, endDate: Date | undefined) => { - if (!startDate || !endDate) return; - let isDateValid = false; const payload = { - start_date: renderFormattedPayloadDate(startDate), - end_date: renderFormattedPayloadDate(endDate), + start_date: renderFormattedPayloadDate(startDate) || null, + end_date: renderFormattedPayloadDate(endDate) || null, }; - if (cycleDetails?.start_date && cycleDetails.end_date) + if (payload?.start_date && payload.end_date) { isDateValid = await dateChecker({ ...payload, cycle_id: cycleDetails.id, }); - else isDateValid = await dateChecker(payload); - + } else { + isDateValid = true; + } if (isDateValid) { submitChanges(payload, "date_range"); setToast({ @@ -175,8 +174,8 @@ export const CycleSidebarHeader: FC = observer((props) => { title: t("project_cycles.action.update.failed.title"), message: t("project_cycles.action.update.error.already_exists"), }); - reset({ ...cycleDetails }); } + return isDateValid; }; const isEditingAllowed = allowPermissions( @@ -296,16 +295,18 @@ export const CycleSidebarHeader: FC = observer((props) => { render={({ field: { value: endDateValue, onChange: onChangeEndDate } }) => ( { - onChangeStartDate(val?.from ? renderFormattedPayloadDate(val.from) : null); - onChangeEndDate(val?.to ? renderFormattedPayloadDate(val.to) : null); - handleDateChange(val?.from, val?.to); + onSelect={async (val) => { + const isDateValid = await handleDateChange(val?.from, val?.to); + if (isDateValid) { + onChangeStartDate(val?.from ? renderFormattedPayloadDate(val.from) : null); + onChangeEndDate(val?.to ? renderFormattedPayloadDate(val.to) : null); + } }} placeholder={{ from: "Start date", diff --git a/web/core/components/cycles/list/cycle-list-item-action.tsx b/web/core/components/cycles/list/cycle-list-item-action.tsx index f44b9bd6c..b7fc40666 100644 --- a/web/core/components/cycles/list/cycle-list-item-action.tsx +++ b/web/core/components/cycles/list/cycle-list-item-action.tsx @@ -3,31 +3,21 @@ import React, { FC, MouseEvent, useEffect, useMemo, useState } from "react"; import { observer } from "mobx-react"; import { useParams, usePathname, useSearchParams } from "next/navigation"; -import { Controller, useForm } from "react-hook-form"; +import { useForm } from "react-hook-form"; import { Eye, Users } from "lucide-react"; // types import { CYCLE_FAVORITED, CYCLE_UNFAVORITED, EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; import { ICycle, TCycleGroups } from "@plane/types"; // ui -import { - Avatar, - AvatarGroup, - FavoriteStar, - LayersIcon, - TOAST_TYPE, - Tooltip, - TransferIcon, - setPromiseToast, - setToast, -} from "@plane/ui"; +import { Avatar, AvatarGroup, FavoriteStar, LayersIcon, Tooltip, TransferIcon, setPromiseToast } from "@plane/ui"; // components import { CycleQuickActions, TransferIssuesModal } from "@/components/cycles"; import { DateRangeDropdown } from "@/components/dropdowns"; import { ButtonAvatars } from "@/components/dropdowns/member/avatar"; // constants // helpers -import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper"; +import { getDate } from "@/helpers/date-time.helper"; import { getFileURL } from "@/helpers/file.helper"; // hooks import { generateQueryParams } from "@/helpers/router.helper"; @@ -36,11 +26,6 @@ import { useAppRouter } from "@/hooks/use-app-router"; import { usePlatformOS } from "@/hooks/use-platform-os"; // plane web components import { CycleAdditionalActions } from "@/plane-web/components/cycles"; -// plane web constants -// services -import { CycleService } from "@/services/cycle.service"; - -const cycleService = new CycleService(); type Props = { workspaceSlug: string; @@ -77,7 +62,7 @@ export const CycleListItemAction: FC = observer((props) => { const { getUserDetails } = useMember(); // form - const { control, reset } = useForm({ + const { control, reset, getValues } = useForm({ defaultValues, }); @@ -98,7 +83,6 @@ export const CycleListItemAction: FC = observer((props) => { workspaceSlug, projectId ); - const renderIcon = Boolean(cycleDetails.start_date) || Boolean(cycleDetails.end_date); // handlers const handleAddToFavorites = (e: MouseEvent) => { @@ -157,54 +141,6 @@ export const CycleListItemAction: FC = observer((props) => { }); }; - const submitChanges = (data: Partial) => { - if (!workspaceSlug || !projectId || !cycleId) return; - updateCycleDetails(workspaceSlug.toString(), projectId.toString(), cycleId.toString(), data); - }; - - const dateChecker = async (payload: any) => { - try { - const res = await cycleService.cycleDateCheck(workspaceSlug as string, projectId as string, payload); - return res.status; - } catch { - return false; - } - }; - - const handleDateChange = async (startDate: Date | undefined, endDate: Date | undefined) => { - if (!startDate || !endDate) return; - - let isDateValid = false; - - const payload = { - start_date: renderFormattedPayloadDate(startDate), - end_date: renderFormattedPayloadDate(endDate), - }; - - if (cycleDetails && cycleDetails.start_date && cycleDetails.end_date) - isDateValid = await dateChecker({ - ...payload, - cycle_id: cycleDetails.id, - }); - else isDateValid = await dateChecker(payload); - - if (isDateValid) { - submitChanges(payload); - setToast({ - type: TOAST_TYPE.SUCCESS, - title: t("project_cycles.action.update.success.title"), - message: t("project_cycles.action.update.success.description"), - }); - } else { - setToast({ - type: TOAST_TYPE.ERROR, - title: t("project_cycles.action.update.failed.title"), - message: t("project_cycles.action.update.error.already_exists"), - }); - reset({ ...cycleDetails }); - } - }; - const createdByDetails = cycleDetails.created_by ? getUserDetails(cycleDetails.created_by) : undefined; useEffect(() => { @@ -214,10 +150,6 @@ export const CycleListItemAction: FC = observer((props) => { }); }, [cycleDetails, reset]); - const isArchived = Boolean(cycleDetails.archived_at); - const isCompleted = cycleStatus === "completed"; - - const isDisabled = !isEditingAllowed || isArchived || isCompleted; // handlers const openCycleOverview = (e: MouseEvent) => { e.preventDefault(); @@ -266,39 +198,27 @@ export const CycleListItemAction: FC = observer((props) => {
)} - {!isActive && ( - ( - ( - { - onChangeStartDate(val?.from ? renderFormattedPayloadDate(val.from) : null); - onChangeEndDate(val?.to ? renderFormattedPayloadDate(val.to) : null); - handleDateChange(val?.from, val?.to); - }} - placeholder={{ - from: "Start date", - to: "End date", - }} - required={cycleDetails.status !== "draft"} - disabled={isDisabled} - hideIcon={{ from: renderIcon ?? true, to: renderIcon }} - /> - )} - /> - )} + {!isActive && cycleDetails.start_date && ( + div]:hover:bg-transparent`} + buttonClassName="p-0" + minDate={new Date()} + value={{ + from: getDate(cycleDetails.start_date), + to: getDate(cycleDetails.end_date), + }} + placeholder={{ + from: "Start date", + to: "End date", + }} + showTooltip + required={cycleDetails.status !== "draft"} + disabled + hideIcon={{ + from: false, + to: false, + }} /> )} diff --git a/web/core/components/dropdowns/date-range.tsx b/web/core/components/dropdowns/date-range.tsx index 7d73216a4..f33a8f8b7 100644 --- a/web/core/components/dropdowns/date-range.tsx +++ b/web/core/components/dropdowns/date-range.tsx @@ -35,7 +35,7 @@ type Props = { }; minDate?: Date; maxDate?: Date; - onSelect: (range: DateRange | undefined) => void; + onSelect?: (range: DateRange | undefined) => void; placeholder?: { from?: string; to?: string; @@ -204,11 +204,7 @@ export const DateRangeDropdown: React.FC = (props) => { classNames={{ root: `p-3 rounded-md` }} selected={dateRange} onSelect={(val) => { - onSelect(val); - setDateRange({ - from: val?.from ?? undefined, - to: val?.to ?? undefined, - }); + onSelect?.(val); }} mode="range" disabled={disabledDays} diff --git a/web/core/components/dropdowns/state.tsx b/web/core/components/dropdowns/state.tsx index 1ac0fe25f..9af77dd7e 100644 --- a/web/core/components/dropdowns/state.tsx +++ b/web/core/components/dropdowns/state.tsx @@ -36,6 +36,7 @@ type Props = TDropdownProps & { stateIds?: string[]; filterAvailableStateIds?: boolean; isForWorkItemCreation?: boolean; + alwaysAllowStateChange?: boolean; }; export const StateDropdown: React.FC = observer((props) => { @@ -59,8 +60,6 @@ export const StateDropdown: React.FC = observer((props) => { value, renderByDefault = true, stateIds, - filterAvailableStateIds = true, - isForWorkItemCreation = false, } = props; // states const [query, setQuery] = useState(""); @@ -235,13 +234,11 @@ export const StateDropdown: React.FC = observer((props) => { filteredOptions.length > 0 ? ( filteredOptions.map((option) => ( )) ) : ( diff --git a/web/core/components/inbox/modals/create-modal/issue-properties.tsx b/web/core/components/inbox/modals/create-modal/issue-properties.tsx index c5bcc965a..e237503f4 100644 --- a/web/core/components/inbox/modals/create-modal/issue-properties.tsx +++ b/web/core/components/inbox/modals/create-modal/issue-properties.tsx @@ -61,6 +61,7 @@ export const InboxIssueProperties: FC = observer((props) projectId={projectId} buttonVariant="border-with-text" tabIndex={getIndex("state_id")} + isForWorkItemCreation={!data?.id} />
diff --git a/web/core/components/issues/attachment/attachment-list-item.tsx b/web/core/components/issues/attachment/attachment-list-item.tsx index 246d69be5..027369405 100644 --- a/web/core/components/issues/attachment/attachment-list-item.tsx +++ b/web/core/components/issues/attachment/attachment-list-item.tsx @@ -66,7 +66,7 @@ export const IssueAttachmentsListItem: FC = observer(
- {attachment?.updated_by && ( + {attachment?.created_by && ( <> = observer((props) => { const { issue: { getIssueById }, subIssues: { subIssuesByIssueId }, - attachment: { getAttachmentsUploadStatusByIssueId }, + attachment: { getAttachmentsCountByIssueId, getAttachmentsUploadStatusByIssueId }, relation: { getRelationCountByIssueId }, } = useIssueDetail(); @@ -41,8 +41,8 @@ export const IssueDetailWidgetCollapsibles: FC = observer((props) => { const shouldRenderRelations = issueRelationsCount > 0; const shouldRenderLinks = !!issue?.link_count && issue?.link_count > 0; const attachmentUploads = getAttachmentsUploadStatusByIssueId(issueId); - const shouldRenderAttachments = - (!!issue?.attachment_count && issue?.attachment_count > 0) || (!!attachmentUploads && attachmentUploads.length > 0); + const attachmentsCount = getAttachmentsCountByIssueId(issueId); + const shouldRenderAttachments = attachmentsCount > 0 || (!!attachmentUploads && attachmentUploads.length > 0); return (
diff --git a/web/core/components/issues/issue-layouts/list/list-group.tsx b/web/core/components/issues/issue-layouts/list/list-group.tsx index 8605dc9dd..99fa9de99 100644 --- a/web/core/components/issues/issue-layouts/list/list-group.tsx +++ b/web/core/components/issues/issue-layouts/list/list-group.tsx @@ -244,6 +244,7 @@ export const ListGroup = observer((props: Props) => { const isDragAllowed = !!group_by && DRAG_ALLOWED_GROUPS.includes(group_by); const canOverlayBeVisible = isWorkflowDropDisabled || orderBy !== "sort_order" || !!group.isDropDisabled; + const isDropDisabled = isWorkflowDropDisabled || !!group.isDropDisabled; const isGroupByCreatedBy = group_by === "created_by"; const shouldExpand = (!!groupIssueCount && isExpanded) || !group_by; @@ -253,7 +254,7 @@ export const ListGroup = observer((props: Props) => { ref={groupRef} className={cn(`relative flex flex-shrink-0 flex-col border-[1px] border-transparent`, { "border-custom-primary-100": isDraggingOverColumn, - "border-custom-error-200": isDraggingOverColumn && !!group.isDropDisabled, + "border-custom-error-200": isDraggingOverColumn && isDropDisabled, })} > { = observer((props) => { /> )} -
-
- - {t("date_range")} -
-
- ( - { - const startDate = getDate(startDateValue); - const endDate = getDate(endDateValue); - return ( - { - onChangeStartDate(val?.from ? renderFormattedPayloadDate(val.from) : null); - onChangeEndDate(val?.to ? renderFormattedPayloadDate(val.to) : null); - handleDateChange(val?.from, val?.to); - }} - placeholder={{ - from: t("start_date"), - to: t("target_date"), - }} - disabled={!isEditingAllowed || isArchived} - /> - ); - }} - /> - )} - /> -
-
-
+
+
+ + {t("date_range")} +
+
+ ( + { + const startDate = getDate(startDateValue); + const endDate = getDate(endDateValue); + return ( + { + onChangeStartDate(val?.from ? renderFormattedPayloadDate(val.from) : null); + onChangeEndDate(val?.to ? renderFormattedPayloadDate(val.to) : null); + handleDateChange(val?.from, val?.to); + }} + placeholder={{ + from: t("start_date"), + to: t("end_date"), + }} + disabled={!isEditingAllowed || isArchived} + /> + ); + }} + /> + )} + /> +
+
diff --git a/web/core/components/modules/module-list-item-action.tsx b/web/core/components/modules/module-list-item-action.tsx index c79860ac4..1f929751d 100644 --- a/web/core/components/modules/module-list-item-action.tsx +++ b/web/core/components/modules/module-list-item-action.tsx @@ -6,7 +6,13 @@ import { useParams } from "next/navigation"; // icons import { SquareUser } from "lucide-react"; // types -import { MODULE_STATUS, MODULE_FAVORITED, MODULE_UNFAVORITED , EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; +import { + MODULE_STATUS, + MODULE_FAVORITED, + MODULE_UNFAVORITED, + EUserPermissions, + EUserPermissionsLevel, +} from "@plane/constants"; import { useTranslation } from "@plane/i18n"; import { IModule } from "@plane/types"; // ui diff --git a/web/core/store/cycle.store.ts b/web/core/store/cycle.store.ts index 4e56e56c1..89221ee7c 100644 --- a/web/core/store/cycle.store.ts +++ b/web/core/store/cycle.store.ts @@ -179,7 +179,9 @@ export class CycleStore implements ICycleStore { const endDate = getDate(c.end_date); const hasEndDatePassed = endDate && isPast(endDate); const isEndDateToday = endDate && isToday(endDate); - return c.project_id === projectId && hasEndDatePassed && !isEndDateToday && !c?.archived_at; + return ( + c.project_id === projectId && ((hasEndDatePassed && !isEndDateToday) || c.status?.toLowerCase() === "completed") + ); }); completedCycles = sortBy(completedCycles, [(c) => c.sort_order]); const completedCycleIds = completedCycles.map((c) => c.id); @@ -195,7 +197,9 @@ export class CycleStore implements ICycleStore { let incompleteCycles = Object.values(this.cycleMap ?? {}).filter((c) => { const endDate = getDate(c.end_date); const hasEndDatePassed = endDate && isPast(endDate); - return c.project_id === projectId && !hasEndDatePassed && !c?.archived_at; + return ( + c.project_id === projectId && !hasEndDatePassed && !c?.archived_at && c.status?.toLowerCase() !== "completed" + ); }); incompleteCycles = sortBy(incompleteCycles, [(c) => c.sort_order]); const incompleteCycleIds = incompleteCycles.map((c) => c.id); diff --git a/web/core/store/issue/issue-details/attachment.store.ts b/web/core/store/issue/issue-details/attachment.store.ts index 73474a3e8..90c28840a 100644 --- a/web/core/store/issue/issue-details/attachment.store.ts +++ b/web/core/store/issue/issue-details/attachment.store.ts @@ -51,6 +51,7 @@ export interface IIssueAttachmentStore extends IIssueAttachmentStoreActions { getAttachmentsUploadStatusByIssueId: (issueId: string) => TAttachmentUploadStatus[] | undefined; getAttachmentsByIssueId: (issueId: string) => string[] | undefined; getAttachmentById: (attachmentId: string) => TIssueAttachment | undefined; + getAttachmentsCountByIssueId: (issueId: string) => number; } export class IssueAttachmentStore implements IIssueAttachmentStore { @@ -109,6 +110,11 @@ export class IssueAttachmentStore implements IIssueAttachmentStore { return this.attachmentMap[attachmentId] ?? undefined; }; + getAttachmentsCountByIssueId = (issueId: string) => { + const attachments = this.getAttachmentsByIssueId(issueId); + return attachments?.length ?? 0; + }; + // actions addAttachments = (issueId: string, attachments: TIssueAttachment[]) => { if (attachments && attachments.length > 0) { @@ -155,12 +161,14 @@ export class IssueAttachmentStore implements IIssueAttachmentStore { this.debouncedUpdateProgress(issueId, tempId, progressPercentage); } ); - const issueAttachmentsCount = this.getAttachmentsByIssueId(issueId)?.length ?? 0; if (response && response.id) { runInAction(() => { update(this.attachments, [issueId], (attachmentIds = []) => uniq(concat(attachmentIds, [response.id]))); set(this.attachmentMap, response.id, response); + this.rootIssueStore.issues.updateIssue(issueId, { + attachment_count: this.getAttachmentsCountByIssueId(issueId), + }); }); } @@ -182,7 +190,6 @@ export class IssueAttachmentStore implements IIssueAttachmentStore { issueId, attachmentId ); - const issueAttachmentsCount = this.getAttachmentsByIssueId(issueId)?.length ?? 1; runInAction(() => { update(this.attachments, [issueId], (attachmentIds = []) => { @@ -191,7 +198,7 @@ export class IssueAttachmentStore implements IIssueAttachmentStore { }); delete this.attachmentMap[attachmentId]; this.rootIssueStore.issues.updateIssue(issueId, { - attachment_count: issueAttachmentsCount - 1, // decrement attachment count + attachment_count: this.getAttachmentsCountByIssueId(issueId), }); }); diff --git a/web/core/store/module.store.ts b/web/core/store/module.store.ts index 06a08b92c..1ed3bf345 100644 --- a/web/core/store/module.store.ts +++ b/web/core/store/module.store.ts @@ -425,7 +425,6 @@ export class ModulesStore implements IModuleStore { set(this.moduleMap, [moduleId], { ...originalModuleDetails, ...data }); }); const response = await this.moduleService.patchModule(workspaceSlug, projectId, moduleId, data); - this.fetchModuleDetails(workspaceSlug, projectId, moduleId); return response; } catch (error) { console.error("Failed to update module in module store", error); diff --git a/web/package.json b/web/package.json index c689d9c3a..697d3abd2 100644 --- a/web/package.json +++ b/web/package.json @@ -1,7 +1,8 @@ { "name": "web", - "version": "0.25.0", + "version": "0.25.1", "private": true, + "license": "AGPL-3.0", "scripts": { "dev": "turbo run develop", "develop": "next dev --port 3000", diff --git a/yarn.lock b/yarn.lock index 5a7f98aa2..051fec566 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10381,11 +10381,6 @@ react-confetti@^6.1.0: dependencies: tween-functions "^1.2.0" -react-day-picker@8.10.1: - version "8.10.1" - resolved "https://registry.yarnpkg.com/react-day-picker/-/react-day-picker-8.10.1.tgz#4762ec298865919b93ec09ba69621580835b8e80" - integrity sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA== - react-day-picker@9.5.0: version "9.5.0" resolved "https://registry.yarnpkg.com/react-day-picker/-/react-day-picker-9.5.0.tgz#2ae36e85d6506026d72e350f49b5607d011cfd6f"