diff --git a/.github/workflows/build-branch.yml b/.github/workflows/build-branch.yml index d25154b15..db65fbc2c 100644 --- a/.github/workflows/build-branch.yml +++ b/.github/workflows/build-branch.yml @@ -9,6 +9,7 @@ on: - preview - qa - develop + - release-* release: types: [released, prereleased] @@ -62,14 +63,14 @@ jobs: runs-on: ubuntu-20.04 needs: [branch_build_setup] env: - FRONTEND_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend:${{ needs.branch_build_setup.outputs.gh_branch_name }} + FRONTEND_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend${{ secrets.DOCKER_REPO_SUFFIX || '' }}:${{ needs.branch_build_setup.outputs.gh_branch_name }} steps: - name: Set Frontend Docker Tag run: | if [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ] && [ "${{ github.event_name }}" == "release" ]; then - TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend:latest,${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend:${{ github.event.release.tag_name }} + TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend${{ secrets.DOCKER_REPO_SUFFIX || '' }}:latest,${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend${{ secrets.DOCKER_REPO_SUFFIX || '' }}:${{ github.event.release.tag_name }} elif [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ]; then - TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend:stable + TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend${{ secrets.DOCKER_REPO_SUFFIX || '' }}:stable else TAG=${{ env.FRONTEND_TAG }} fi @@ -104,14 +105,14 @@ jobs: runs-on: ubuntu-20.04 needs: [branch_build_setup] env: - SPACE_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/plane-space:${{ needs.branch_build_setup.outputs.gh_branch_name }} + SPACE_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/plane-space${{ secrets.DOCKER_REPO_SUFFIX || '' }}:${{ needs.branch_build_setup.outputs.gh_branch_name }} steps: - name: Set Space Docker Tag run: | if [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ] && [ "${{ github.event_name }}" == "release" ]; then - TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-space:latest,${{ secrets.DOCKERHUB_USERNAME }}/plane-space:${{ github.event.release.tag_name }} + TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-space${{ secrets.DOCKER_REPO_SUFFIX || '' }}:latest,${{ secrets.DOCKERHUB_USERNAME }}/plane-space${{ secrets.DOCKER_REPO_SUFFIX || '' }}:${{ github.event.release.tag_name }} elif [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ]; then - TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-space:stable + TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-space${{ secrets.DOCKER_REPO_SUFFIX || '' }}:stable else TAG=${{ env.SPACE_TAG }} fi @@ -146,14 +147,14 @@ jobs: runs-on: ubuntu-20.04 needs: [branch_build_setup] env: - BACKEND_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/plane-backend:${{ needs.branch_build_setup.outputs.gh_branch_name }} + BACKEND_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/plane-backend${{ secrets.DOCKER_REPO_SUFFIX || '' }}:${{ needs.branch_build_setup.outputs.gh_branch_name }} steps: - name: Set Backend Docker Tag run: | if [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ] && [ "${{ github.event_name }}" == "release" ]; then - TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-backend:latest,${{ secrets.DOCKERHUB_USERNAME }}/plane-backend:${{ github.event.release.tag_name }} + TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-backend${{ secrets.DOCKER_REPO_SUFFIX || '' }}:latest,${{ secrets.DOCKERHUB_USERNAME }}/plane-backend${{ secrets.DOCKER_REPO_SUFFIX || '' }}:${{ github.event.release.tag_name }} elif [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ]; then - TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-backend:stable + TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-backend${{ secrets.DOCKER_REPO_SUFFIX || '' }}:stable else TAG=${{ env.BACKEND_TAG }} fi @@ -188,14 +189,14 @@ jobs: runs-on: ubuntu-20.04 needs: [branch_build_setup] env: - PROXY_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy:${{ needs.branch_build_setup.outputs.gh_branch_name }} + PROXY_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy${{ secrets.DOCKER_REPO_SUFFIX || '' }}:${{ needs.branch_build_setup.outputs.gh_branch_name }} steps: - name: Set Proxy Docker Tag run: | if [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ] && [ "${{ github.event_name }}" == "release" ]; then - TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy:latest,${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy:${{ github.event.release.tag_name }} + TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy${{ secrets.DOCKER_REPO_SUFFIX || '' }}:latest,${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy${{ secrets.DOCKER_REPO_SUFFIX || '' }}:${{ github.event.release.tag_name }} elif [ "${{ needs.branch_build_setup.outputs.gh_branch_name }}" == "master" ]; then - TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy:stable + TAG=${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy${{ secrets.DOCKER_REPO_SUFFIX || '' }}:stable else TAG=${{ env.PROXY_TAG }} fi diff --git a/.github/workflows/build-test-pull-request.yml b/.github/workflows/build-test-pull-request.yml index c74975f48..fd5d5ad03 100644 --- a/.github/workflows/build-test-pull-request.yml +++ b/.github/workflows/build-test-pull-request.yml @@ -1,6 +1,6 @@ name: Build Pull Request Contents -on: +on: pull_request: types: ["opened", "synchronize"] @@ -14,13 +14,15 @@ jobs: steps: - name: Checkout Repository to Actions uses: actions/checkout@v3.3.0 + with: + token: ${{ secrets.ACCESS_TOKEN }} - name: Setup Node.js 18.x uses: actions/setup-node@v2 with: node-version: 18.x - cache: 'yarn' - + cache: "yarn" + - name: Get changed files id: changed-files uses: tj-actions/changed-files@v38 @@ -44,5 +46,3 @@ jobs: run: | yarn yarn build --filter=space - - diff --git a/apiserver/plane/api/serializers/issue.py b/apiserver/plane/api/serializers/issue.py index ab61ae523..75396e9bb 100644 --- a/apiserver/plane/api/serializers/issue.py +++ b/apiserver/plane/api/serializers/issue.py @@ -96,7 +96,7 @@ class IssueSerializer(BaseSerializer): if ( data.get("state") and not State.objects.filter( - project_id=self.context.get("project_id"), pk=data.get("state") + project_id=self.context.get("project_id"), pk=data.get("state").id ).exists() ): raise serializers.ValidationError( @@ -107,7 +107,7 @@ class IssueSerializer(BaseSerializer): if ( data.get("parent") and not Issue.objects.filter( - workspace_id=self.context.get("workspace_id"), pk=data.get("parent") + workspace_id=self.context.get("workspace_id"), pk=data.get("parent").id ).exists() ): raise serializers.ValidationError( diff --git a/apiserver/plane/api/serializers/module.py b/apiserver/plane/api/serializers/module.py index 65710e8af..a96a9b54d 100644 --- a/apiserver/plane/api/serializers/module.py +++ b/apiserver/plane/api/serializers/module.py @@ -65,18 +65,18 @@ class ModuleSerializer(BaseSerializer): def create(self, validated_data): members = validated_data.pop("members", None) - project = self.context["project"] - - module = Module.objects.create(**validated_data, project=project) + project_id = self.context["project_id"] + workspace_id = self.context["workspace_id"] + module = Module.objects.create(**validated_data, project_id=project_id) if members is not None: ModuleMember.objects.bulk_create( [ ModuleMember( module=module, - member=member, - project=project, - workspace=project.workspace, + member_id=str(member), + project_id=project_id, + workspace_id=workspace_id, created_by=module.created_by, updated_by=module.updated_by, ) @@ -97,7 +97,7 @@ class ModuleSerializer(BaseSerializer): [ ModuleMember( module=instance, - member=member, + member_id=str(member), project=instance.project, workspace=instance.project.workspace, created_by=instance.created_by, diff --git a/apiserver/plane/api/views/issue.py b/apiserver/plane/api/views/issue.py index 41745010f..1ac8ddcff 100644 --- a/apiserver/plane/api/views/issue.py +++ b/apiserver/plane/api/views/issue.py @@ -221,11 +221,20 @@ class IssueAPIEndpoint(WebhookMixin, BaseAPIView): def patch(self, request, slug, project_id, pk=None): issue = Issue.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) + project = Project.objects.get(pk=project_id) current_instance = json.dumps( IssueSerializer(issue).data, cls=DjangoJSONEncoder ) requested_data = json.dumps(self.request.data, cls=DjangoJSONEncoder) - serializer = IssueSerializer(issue, data=request.data, partial=True) + serializer = IssueSerializer( + issue, + data=request.data, + context={ + "project_id": project_id, + "workspace_id": project.workspace_id, + }, + partial=True, + ) if serializer.is_valid(): serializer.save() issue_activity.delay( diff --git a/apiserver/plane/api/views/module.py b/apiserver/plane/api/views/module.py index 221c7f31b..959b7ccc3 100644 --- a/apiserver/plane/api/views/module.py +++ b/apiserver/plane/api/views/module.py @@ -121,8 +121,8 @@ class ModuleAPIEndpoint(WebhookMixin, BaseAPIView): ) def post(self, request, slug, project_id): - project = Project.objects.get(workspace__slug=slug, pk=project_id) - serializer = ModuleSerializer(data=request.data, context={"project": project}) + project = Project.objects.get(pk=project_id, workspace__slug=slug) + serializer = ModuleSerializer(data=request.data, context={"project_id": project_id, "workspace_id": project.workspace_id}) if serializer.is_valid(): serializer.save() module = Module.objects.get(pk=serializer.data["id"]) @@ -132,7 +132,7 @@ class ModuleAPIEndpoint(WebhookMixin, BaseAPIView): def patch(self, request, slug, project_id, pk): module = Module.objects.get(pk=pk, project_id=project_id, workspace__slug=slug) - serializer = ModuleSerializer(module, data=request.data) + serializer = ModuleSerializer(module, data=request.data, context={"project_id": project_id}, partial=True) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) diff --git a/apiserver/plane/app/serializers/cycle.py b/apiserver/plane/app/serializers/cycle.py index 104a3dd06..63abf3a03 100644 --- a/apiserver/plane/app/serializers/cycle.py +++ b/apiserver/plane/app/serializers/cycle.py @@ -40,6 +40,7 @@ class CycleSerializer(BaseSerializer): started_estimates = serializers.IntegerField(read_only=True) workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace") project_detail = ProjectLiteSerializer(read_only=True, source="project") + status = serializers.CharField(read_only=True) def validate(self, data): if ( diff --git a/apiserver/plane/app/serializers/estimate.py b/apiserver/plane/app/serializers/estimate.py index 4a1cda779..2c2f26e4e 100644 --- a/apiserver/plane/app/serializers/estimate.py +++ b/apiserver/plane/app/serializers/estimate.py @@ -4,6 +4,7 @@ from .base import BaseSerializer from plane.db.models import Estimate, EstimatePoint from plane.app.serializers import WorkspaceLiteSerializer, ProjectLiteSerializer +from rest_framework import serializers class EstimateSerializer(BaseSerializer): workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace") @@ -19,6 +20,15 @@ class EstimateSerializer(BaseSerializer): class EstimatePointSerializer(BaseSerializer): + + def validate(self, data): + if not data: + raise serializers.ValidationError("Estimate points are required") + value = data.get("value") + if value and len(value) > 20: + raise serializers.ValidationError("Value can't be more than 20 characters") + return data + class Meta: model = EstimatePoint fields = "__all__" diff --git a/apiserver/plane/app/views/cycle.py b/apiserver/plane/app/views/cycle.py index d2f82d75b..02f259de3 100644 --- a/apiserver/plane/app/views/cycle.py +++ b/apiserver/plane/app/views/cycle.py @@ -11,6 +11,10 @@ from django.db.models import ( Count, Prefetch, Sum, + Case, + When, + Value, + CharField ) from django.core import serializers from django.utils import timezone @@ -157,6 +161,28 @@ class CycleViewSet(WebhookMixin, BaseViewSet): ), ) ) + .annotate( + status=Case( + When( + Q(start_date__lte=timezone.now()) & Q(end_date__gte=timezone.now()), + then=Value("CURRENT") + ), + When( + start_date__gt=timezone.now(), + then=Value("UPCOMING") + ), + When( + end_date__lt=timezone.now(), + then=Value("COMPLETED") + ), + When( + Q(start_date__isnull=True) & Q(end_date__isnull=True), + then=Value("DRAFT") + ), + default=Value("DRAFT"), + output_field=CharField(), + ) + ) .prefetch_related( Prefetch( "issue_cycle__issue__assignees", @@ -177,7 +203,7 @@ class CycleViewSet(WebhookMixin, BaseViewSet): queryset = self.get_queryset() cycle_view = request.GET.get("cycle_view", "all") - queryset = queryset.order_by("-is_favorite","-created_at") + queryset = queryset.order_by("-is_favorite", "-created_at") # Current Cycle if cycle_view == "current": @@ -575,7 +601,9 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet): ) ) - issues = IssueStateSerializer(issues, many=True, fields=fields if fields else None).data + issues = IssueStateSerializer( + issues, many=True, fields=fields if fields else None + ).data issue_dict = {str(issue["id"]): issue for issue in issues} return Response(issue_dict, status=status.HTTP_200_OK) @@ -805,4 +833,4 @@ class TransferCycleIssueEndpoint(BaseAPIView): updated_cycles, ["cycle_id"], batch_size=100 ) - return Response({"message": "Success"}, status=status.HTTP_200_OK) \ No newline at end of file + return Response({"message": "Success"}, status=status.HTTP_200_OK) diff --git a/apiserver/plane/app/views/estimate.py b/apiserver/plane/app/views/estimate.py index ec9393f5b..8f14b230b 100644 --- a/apiserver/plane/app/views/estimate.py +++ b/apiserver/plane/app/views/estimate.py @@ -53,11 +53,11 @@ class BulkEstimatePointEndpoint(BaseViewSet): ) estimate_points = request.data.get("estimate_points", []) - - if not len(estimate_points) or len(estimate_points) > 8: + + serializer = EstimatePointSerializer(data=request.data.get("estimate_points"), many=True) + if not serializer.is_valid(): return Response( - {"error": "Estimate points are required"}, - status=status.HTTP_400_BAD_REQUEST, + serializer.errors, status=status.HTTP_400_BAD_REQUEST ) estimate_serializer = EstimateSerializer(data=request.data.get("estimate")) diff --git a/apiserver/plane/app/views/search.py b/apiserver/plane/app/views/search.py index ac560643a..4ecb71127 100644 --- a/apiserver/plane/app/views/search.py +++ b/apiserver/plane/app/views/search.py @@ -50,7 +50,8 @@ class GlobalSearchEndpoint(BaseAPIView): q = Q() for field in fields: if field == "sequence_id": - sequences = re.findall(r"\d+\.\d+|\d+", query) + # Match whole integers only (exclude decimal numbers) + sequences = re.findall(r"\b\d+\b", query) for sequence_id in sequences: q |= Q(**{"sequence_id": sequence_id}) else: diff --git a/apiserver/plane/bgtasks/issue_activites_task.py b/apiserver/plane/bgtasks/issue_activites_task.py index 3b2b40223..5d4c0650c 100644 --- a/apiserver/plane/bgtasks/issue_activites_task.py +++ b/apiserver/plane/bgtasks/issue_activites_task.py @@ -112,8 +112,8 @@ def track_parent( epoch, ): if current_instance.get("parent") != requested_data.get("parent"): - old_parent = Issue.objects.filter(pk=current_instance.get("parent")).first() - new_parent = Issue.objects.filter(pk=requested_data.get("parent")).first() + old_parent = Issue.objects.filter(pk=current_instance.get("parent")).first() if current_instance.get("parent") is not None else None + new_parent = Issue.objects.filter(pk=requested_data.get("parent")).first() if requested_data.get("parent") is not None else None issue_activities.append( IssueActivity( diff --git a/apiserver/plane/db/migrations/0051_cycle_external_id_cycle_external_source_and_more.py b/apiserver/plane/db/migrations/0051_cycle_external_id_cycle_external_source_and_more.py new file mode 100644 index 000000000..19267dfc2 --- /dev/null +++ b/apiserver/plane/db/migrations/0051_cycle_external_id_cycle_external_source_and_more.py @@ -0,0 +1,83 @@ +# Generated by Django 4.2.7 on 2023-12-29 10:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('db', '0050_user_use_case_alter_workspace_organization_size'), + ] + + operations = [ + migrations.AddField( + model_name='cycle', + name='external_id', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='cycle', + name='external_source', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='inboxissue', + name='external_id', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='inboxissue', + name='external_source', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='issue', + name='external_id', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='issue', + name='external_source', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='issuecomment', + name='external_id', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='issuecomment', + name='external_source', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='label', + name='external_id', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='label', + name='external_source', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='module', + name='external_id', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='module', + name='external_source', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='state', + name='external_id', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='state', + name='external_source', + field=models.CharField(blank=True, max_length=255, null=True), + ), + ] diff --git a/apiserver/plane/db/models/cycle.py b/apiserver/plane/db/models/cycle.py index 56301e3d3..e5e2c355b 100644 --- a/apiserver/plane/db/models/cycle.py +++ b/apiserver/plane/db/models/cycle.py @@ -18,6 +18,8 @@ class Cycle(ProjectBaseModel): ) view_props = models.JSONField(default=dict) sort_order = models.FloatField(default=65535) + external_source = models.CharField(max_length=255, null=True, blank=True) + external_id = models.CharField(max_length=255, blank=True, null=True) class Meta: verbose_name = "Cycle" diff --git a/apiserver/plane/db/models/inbox.py b/apiserver/plane/db/models/inbox.py index 497a20f00..6ad88e681 100644 --- a/apiserver/plane/db/models/inbox.py +++ b/apiserver/plane/db/models/inbox.py @@ -39,6 +39,8 @@ class InboxIssue(ProjectBaseModel): "db.Issue", related_name="inbox_duplicate", on_delete=models.SET_NULL, null=True ) source = models.TextField(blank=True, null=True) + external_source = models.CharField(max_length=255, null=True, blank=True) + external_id = models.CharField(max_length=255, blank=True, null=True) class Meta: verbose_name = "InboxIssue" diff --git a/apiserver/plane/db/models/issue.py b/apiserver/plane/db/models/issue.py index 9b293a75d..54acd5c5d 100644 --- a/apiserver/plane/db/models/issue.py +++ b/apiserver/plane/db/models/issue.py @@ -102,6 +102,8 @@ class Issue(ProjectBaseModel): completed_at = models.DateTimeField(null=True) archived_at = models.DateField(null=True) is_draft = models.BooleanField(default=False) + external_source = models.CharField(max_length=255, null=True, blank=True) + external_id = models.CharField(max_length=255, blank=True, null=True) objects = models.Manager() issue_objects = IssueManager() @@ -366,6 +368,8 @@ class IssueComment(ProjectBaseModel): default="INTERNAL", max_length=100, ) + external_source = models.CharField(max_length=255, null=True, blank=True) + external_id = models.CharField(max_length=255, blank=True, null=True) def save(self, *args, **kwargs): self.comment_stripped = ( @@ -416,6 +420,8 @@ class Label(ProjectBaseModel): description = models.TextField(blank=True) color = models.CharField(max_length=255, blank=True) sort_order = models.FloatField(default=65535) + external_source = models.CharField(max_length=255, null=True, blank=True) + external_id = models.CharField(max_length=255, blank=True, null=True) class Meta: unique_together = ["name", "project"] diff --git a/apiserver/plane/db/models/module.py b/apiserver/plane/db/models/module.py index ae540cc6c..e485eea62 100644 --- a/apiserver/plane/db/models/module.py +++ b/apiserver/plane/db/models/module.py @@ -41,6 +41,8 @@ class Module(ProjectBaseModel): ) view_props = models.JSONField(default=dict) sort_order = models.FloatField(default=65535) + external_source = models.CharField(max_length=255, null=True, blank=True) + external_id = models.CharField(max_length=255, blank=True, null=True) class Meta: unique_together = ["name", "project"] diff --git a/apiserver/plane/db/models/state.py b/apiserver/plane/db/models/state.py index 2fa1ebe38..3370f239d 100644 --- a/apiserver/plane/db/models/state.py +++ b/apiserver/plane/db/models/state.py @@ -24,6 +24,8 @@ class State(ProjectBaseModel): max_length=20, ) default = models.BooleanField(default=False) + external_source = models.CharField(max_length=255, null=True, blank=True) + external_id = models.CharField(max_length=255, blank=True, null=True) def __str__(self): """Return name of the state""" diff --git a/apiserver/plane/settings/common.py b/apiserver/plane/settings/common.py index 76528176b..971ed5543 100644 --- a/apiserver/plane/settings/common.py +++ b/apiserver/plane/settings/common.py @@ -291,7 +291,9 @@ CELERY_IMPORTS = ( # Sentry Settings # Enable Sentry Settings -if bool(os.environ.get("SENTRY_DSN", False)) and os.environ.get("SENTRY_DSN").startswith("https://"): +if bool(os.environ.get("SENTRY_DSN", False)) and os.environ.get( + "SENTRY_DSN" +).startswith("https://"): sentry_sdk.init( dsn=os.environ.get("SENTRY_DSN", ""), integrations=[ @@ -334,3 +336,5 @@ INSTANCE_KEY = os.environ.get( # Skip environment variable configuration SKIP_ENV_VAR = os.environ.get("SKIP_ENV_VAR", "1") == "1" + +DATA_UPLOAD_MAX_MEMORY_SIZE = int(os.environ.get("FILE_SIZE_LIMIT", 5242880)) diff --git a/nginx/nginx.conf.dev b/nginx/nginx.conf.dev index c78893f9f..182fc4d83 100644 --- a/nginx/nginx.conf.dev +++ b/nginx/nginx.conf.dev @@ -18,6 +18,9 @@ http { location / { proxy_pass http://web:3000/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; } location /api/ { diff --git a/package.json b/package.json index 06e84557b..b5d997662 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "prettier": "latest", "prettier-plugin-tailwindcss": "^0.5.4", "tailwindcss": "^3.3.3", - "turbo": "^1.11.1" + "turbo": "^1.11.2" }, "resolutions": { "@types/react": "18.2.42" diff --git a/packages/editor/core/package.json b/packages/editor/core/package.json index 2f458995c..ef2be61e3 100644 --- a/packages/editor/core/package.json +++ b/packages/editor/core/package.json @@ -28,28 +28,22 @@ "react-dom": "18.2.0" }, "dependencies": { - "@plane/editor-types": "*", - "@tiptap/core": "^2.1.7", + "@tiptap/core": "^2.1.13", "@tiptap/extension-blockquote": "^2.1.13", - "@tiptap/extension-code-block-lowlight": "^2.1.12", - "@tiptap/extension-color": "^2.1.11", - "@tiptap/extension-image": "^2.1.7", - "@tiptap/extension-link": "^2.1.7", - "@tiptap/extension-list-item": "^2.1.12", - "@tiptap/extension-mention": "^2.1.12", - "@tiptap/extension-table": "^2.1.6", - "@tiptap/extension-table-cell": "^2.1.6", - "@tiptap/extension-table-header": "^2.1.6", - "@tiptap/extension-table-row": "^2.1.6", - "@tiptap/extension-task-item": "^2.1.7", - "@tiptap/extension-task-list": "^2.1.7", - "@tiptap/extension-text-style": "^2.1.11", - "@tiptap/extension-underline": "^2.1.7", - "@tiptap/pm": "^2.1.7", - "@tiptap/prosemirror-tables": "^1.1.4", - "@tiptap/react": "^2.1.7", - "@tiptap/starter-kit": "^2.1.10", - "@tiptap/suggestion": "^2.0.4", + "@tiptap/extension-code-block-lowlight": "^2.1.13", + "@tiptap/extension-color": "^2.1.13", + "@tiptap/extension-image": "^2.1.13", + "@tiptap/extension-link": "^2.1.13", + "@tiptap/extension-list-item": "^2.1.13", + "@tiptap/extension-mention": "^2.1.13", + "@tiptap/extension-task-item": "^2.1.13", + "@tiptap/extension-task-list": "^2.1.13", + "@tiptap/extension-text-style": "^2.1.13", + "@tiptap/extension-underline": "^2.1.13", + "@tiptap/pm": "^2.1.13", + "@tiptap/react": "^2.1.13", + "@tiptap/starter-kit": "^2.1.13", + "@tiptap/suggestion": "^2.0.13", "class-variance-authority": "^0.7.0", "clsx": "^1.2.1", "highlight.js": "^11.8.0", diff --git a/packages/editor/core/src/ui/hooks/use-editor.tsx b/packages/editor/core/src/hooks/use-editor.tsx similarity index 86% rename from packages/editor/core/src/ui/hooks/use-editor.tsx rename to packages/editor/core/src/hooks/use-editor.tsx index 149f9b453..c2923c1e9 100644 --- a/packages/editor/core/src/ui/hooks/use-editor.tsx +++ b/packages/editor/core/src/hooks/use-editor.tsx @@ -1,10 +1,13 @@ import { useEditor as useCustomEditor, Editor } from "@tiptap/react"; import { useImperativeHandle, useRef, MutableRefObject } from "react"; -import { CoreEditorProps } from "../props"; -import { CoreEditorExtensions } from "../extensions"; +import { CoreEditorProps } from "src/ui/props"; +import { CoreEditorExtensions } from "src/ui/extensions"; import { EditorProps } from "@tiptap/pm/view"; -import { getTrimmedHTML } from "../../lib/utils"; -import { DeleteImage, IMentionSuggestion, RestoreImage, UploadImage } from "@plane/editor-types"; +import { getTrimmedHTML } from "src/lib/utils"; +import { DeleteImage } from "src/types/delete-image"; +import { IMentionSuggestion } from "src/types/mention-suggestion"; +import { RestoreImage } from "src/types/restore-image"; +import { UploadImage } from "src/types/upload-image"; interface CustomEditorProps { uploadFile: UploadImage; diff --git a/packages/editor/core/src/ui/hooks/use-read-only-editor.tsx b/packages/editor/core/src/hooks/use-read-only-editor.tsx similarity index 88% rename from packages/editor/core/src/ui/hooks/use-read-only-editor.tsx rename to packages/editor/core/src/hooks/use-read-only-editor.tsx index 5c2429108..ecd49255c 100644 --- a/packages/editor/core/src/ui/hooks/use-read-only-editor.tsx +++ b/packages/editor/core/src/hooks/use-read-only-editor.tsx @@ -1,9 +1,9 @@ import { useEditor as useCustomEditor, Editor } from "@tiptap/react"; import { useImperativeHandle, useRef, MutableRefObject } from "react"; -import { CoreReadOnlyEditorExtensions } from "../read-only/extensions"; -import { CoreReadOnlyEditorProps } from "../read-only/props"; +import { CoreReadOnlyEditorExtensions } from "src/ui/read-only/extensions"; +import { CoreReadOnlyEditorProps } from "src/ui/read-only/props"; import { EditorProps } from "@tiptap/pm/view"; -import { IMentionSuggestion } from "@plane/editor-types"; +import { IMentionSuggestion } from "src/types/mention-suggestion"; interface CustomReadOnlyEditorProps { value: string; diff --git a/packages/editor/core/src/index.ts b/packages/editor/core/src/index.ts index bdf533193..c7e39d240 100644 --- a/packages/editor/core/src/index.ts +++ b/packages/editor/core/src/index.ts @@ -1,23 +1,32 @@ // styles // import "./styles/tailwind.css"; -// import "./styles/editor.css"; -import "./styles/github-dark.css"; +import "src/styles/editor.css"; +import "src/styles/table.css"; +import "src/styles/github-dark.css"; -export { isCellSelection } from "./ui/extensions/table/table/utilities/is-cell-selection"; +export { isCellSelection } from "src/ui/extensions/table/table/utilities/is-cell-selection"; // utils -export * from "./lib/utils"; -export * from "./ui/extensions/table/table"; -export { startImageUpload } from "./ui/plugins/upload-image"; +export * from "src/lib/utils"; +export * from "src/ui/extensions/table/table"; +export { startImageUpload } from "src/ui/plugins/upload-image"; // components -export { EditorContainer } from "./ui/components/editor-container"; -export { EditorContentWrapper } from "./ui/components/editor-content"; +export { EditorContainer } from "src/ui/components/editor-container"; +export { EditorContentWrapper } from "src/ui/components/editor-content"; // hooks -export { useEditor } from "./ui/hooks/use-editor"; -export { useReadOnlyEditor } from "./ui/hooks/use-read-only-editor"; +export { useEditor } from "src/hooks/use-editor"; +export { useReadOnlyEditor } from "src/hooks/use-read-only-editor"; // helper items -export * from "./ui/menus/menu-items"; -export * from "./lib/editor-commands"; +export * from "src/ui/menus/menu-items"; +export * from "src/lib/editor-commands"; + +// types +export type { DeleteImage } from "src/types/delete-image"; +export type { UploadImage } from "src/types/upload-image"; +export type { RestoreImage } from "src/types/restore-image"; +export type { IMentionHighlight, IMentionSuggestion } from "src/types/mention-suggestion"; +export type { ISlashCommandItem, CommandProps } from "src/types/slash-commands-suggestion"; +export type { LucideIconType } from "src/types/lucide-icon"; diff --git a/packages/editor/core/src/lib/editor-commands.ts b/packages/editor/core/src/lib/editor-commands.ts index 4a331e7cd..147797e2d 100644 --- a/packages/editor/core/src/lib/editor-commands.ts +++ b/packages/editor/core/src/lib/editor-commands.ts @@ -1,7 +1,7 @@ -import { UploadImage } from "@plane/editor-types"; import { Editor, Range } from "@tiptap/core"; -import { startImageUpload } from "../ui/plugins/upload-image"; -import { findTableAncestor } from "./utils"; +import { startImageUpload } from "src/ui/plugins/upload-image"; +import { findTableAncestor } from "src/lib/utils"; +import { UploadImage } from "src/types/upload-image"; export const toggleHeadingOne = (editor: Editor, range?: Range) => { if (range) editor.chain().focus().deleteRange(range).setNode("heading", { level: 1 }).run(); diff --git a/packages/editor/core/src/styles/editor.css b/packages/editor/core/src/styles/editor.css index 85d881eeb..86822664b 100644 --- a/packages/editor/core/src/styles/editor.css +++ b/packages/editor/core/src/styles/editor.css @@ -6,6 +6,12 @@ height: 0; } +/* block quotes */ +.ProseMirror blockquote p::before, +.ProseMirror blockquote p::after { + display: none; +} + .ProseMirror .is-empty::before { content: attr(data-placeholder); float: left; @@ -15,9 +21,10 @@ } /* Custom image styles */ - .ProseMirror img { transition: filter 0.1s ease-in-out; + margin-top: 0 !important; + margin-bottom: 0 !important; &:hover { cursor: pointer; @@ -53,11 +60,12 @@ ul[data-type="taskList"] li > label input[type="checkbox"] { background-color: rgb(var(--color-background-100)); margin: 0; cursor: pointer; - width: 1.2rem; - height: 1.2rem; + width: 0.8rem; + height: 0.8rem; position: relative; - border: 2px solid rgb(var(--color-text-100)); - margin-right: 0.3rem; + border: 1.5px solid rgb(var(--color-text-100)); + margin-right: 0.2rem; + margin-top: 0.15rem; display: grid; place-content: center; @@ -71,8 +79,8 @@ ul[data-type="taskList"] li > label input[type="checkbox"] { &::before { content: ""; - width: 0.65em; - height: 0.65em; + width: 0.5em; + height: 0.5em; transform: scale(0); transition: 120ms transform ease-in-out; box-shadow: inset 1em 1em; @@ -133,6 +141,8 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p { .img-placeholder { position: relative; width: 35%; + margin-top: 0 !important; + margin-bottom: 0 !important; &:before { content: ""; @@ -159,7 +169,8 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p { table { border-collapse: collapse; table-layout: fixed; - margin: 0; + margin: 0.5em 0 0.5em 0; + border: 1px solid rgb(var(--color-border-200)); width: 100%; @@ -229,3 +240,34 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p { .ProseMirror table * .is-empty::before { opacity: 0; } + +.ProseMirror pre { + background: rgba(var(--color-background-80)); + border-radius: 0.5rem; + color: rgba(var(--color-text-100)); + font-family: "JetBrainsMono", monospace; + padding: 0.75rem 1rem; +} + +.ProseMirror pre code { + background: none; + color: inherit; + font-size: 0.8rem; + padding: 0; +} + +div[data-type="horizontalRule"] { + line-height: 0; + padding: 0.25rem 0; + margin-top: 0; + margin-bottom: 0; + + & > div { + border-bottom: 1px solid rgb(var(--color-text-100)); + } +} + +/* image resizer */ +.moveable-control-box { + z-index: 10 !important; +} diff --git a/space/styles/table.css b/packages/editor/core/src/styles/table.css similarity index 100% rename from space/styles/table.css rename to packages/editor/core/src/styles/table.css diff --git a/packages/editor/types/src/types/delete-image.ts b/packages/editor/core/src/types/delete-image.ts similarity index 100% rename from packages/editor/types/src/types/delete-image.ts rename to packages/editor/core/src/types/delete-image.ts diff --git a/packages/editor/core/src/types/lucide-icon.ts b/packages/editor/core/src/types/lucide-icon.ts new file mode 100644 index 000000000..2211c18e8 --- /dev/null +++ b/packages/editor/core/src/types/lucide-icon.ts @@ -0,0 +1,3 @@ +import { Smile } from "lucide-react"; + +export type LucideIconType = typeof Smile; diff --git a/packages/editor/types/src/types/mention-suggestion.ts b/packages/editor/core/src/types/mention-suggestion.ts similarity index 100% rename from packages/editor/types/src/types/mention-suggestion.ts rename to packages/editor/core/src/types/mention-suggestion.ts diff --git a/packages/editor/types/src/types/restore-image.ts b/packages/editor/core/src/types/restore-image.ts similarity index 100% rename from packages/editor/types/src/types/restore-image.ts rename to packages/editor/core/src/types/restore-image.ts diff --git a/packages/editor/types/src/types/slash-commands-suggestion.ts b/packages/editor/core/src/types/slash-commands-suggestion.ts similarity index 100% rename from packages/editor/types/src/types/slash-commands-suggestion.ts rename to packages/editor/core/src/types/slash-commands-suggestion.ts diff --git a/packages/editor/types/src/types/upload-image.ts b/packages/editor/core/src/types/upload-image.ts similarity index 100% rename from packages/editor/types/src/types/upload-image.ts rename to packages/editor/core/src/types/upload-image.ts diff --git a/packages/editor/core/src/ui/components/editor-content.tsx b/packages/editor/core/src/ui/components/editor-content.tsx index f66edbb12..9c0938788 100644 --- a/packages/editor/core/src/ui/components/editor-content.tsx +++ b/packages/editor/core/src/ui/components/editor-content.tsx @@ -1,6 +1,6 @@ import { Editor, EditorContent } from "@tiptap/react"; import { ReactNode } from "react"; -import { ImageResizer } from "../extensions/image/image-resize"; +import { ImageResizer } from "src/ui/extensions/image/image-resize"; interface EditorContentProps { editor: Editor | null; diff --git a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/get-next-list-depth.ts b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/get-next-list-depth.ts index f7583f195..2e4f5fbaa 100644 --- a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/get-next-list-depth.ts +++ b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/get-next-list-depth.ts @@ -1,7 +1,7 @@ import { getNodeAtPosition } from "@tiptap/core"; import { EditorState } from "@tiptap/pm/state"; -import { findListItemPos } from "./find-list-item-pos"; +import { findListItemPos } from "src/ui/extensions/custom-list-keymap/list-helpers/find-list-item-pos"; export const getNextListDepth = (typeOrName: string, state: EditorState) => { const listItemPos = findListItemPos(typeOrName, state); diff --git a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/handle-backspace.ts b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/handle-backspace.ts index 08906148b..a4f2d5db9 100644 --- a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/handle-backspace.ts +++ b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/handle-backspace.ts @@ -1,8 +1,8 @@ import { Editor, isAtStartOfNode, isNodeActive } from "@tiptap/core"; import { Node } from "@tiptap/pm/model"; -import { findListItemPos } from "./find-list-item-pos"; -import { hasListBefore } from "./has-list-before"; +import { findListItemPos } from "src/ui/extensions/custom-list-keymap/list-helpers/find-list-item-pos"; +import { hasListBefore } from "src/ui/extensions/custom-list-keymap/list-helpers/has-list-before"; export const handleBackspace = (editor: Editor, name: string, parentListTypes: string[]) => { // this is required to still handle the undo handling diff --git a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/handle-delete.ts b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/handle-delete.ts index 5f47baf9d..9179e0f20 100644 --- a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/handle-delete.ts +++ b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/handle-delete.ts @@ -1,7 +1,7 @@ import { Editor, isAtEndOfNode, isNodeActive } from "@tiptap/core"; -import { nextListIsDeeper } from "./next-list-is-deeper"; -import { nextListIsHigher } from "./next-list-is-higher"; +import { nextListIsDeeper } from "src/ui/extensions/custom-list-keymap/list-helpers/next-list-is-deeper"; +import { nextListIsHigher } from "src/ui/extensions/custom-list-keymap/list-helpers/next-list-is-higher"; export const handleDelete = (editor: Editor, name: string) => { // if the cursor is not inside the current node type diff --git a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/next-list-is-deeper.ts b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/next-list-is-deeper.ts index 425458b2a..7cd1a63f7 100644 --- a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/next-list-is-deeper.ts +++ b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/next-list-is-deeper.ts @@ -1,7 +1,7 @@ import { EditorState } from "@tiptap/pm/state"; -import { findListItemPos } from "./find-list-item-pos"; -import { getNextListDepth } from "./get-next-list-depth"; +import { findListItemPos } from "src/ui/extensions/custom-list-keymap/list-helpers/find-list-item-pos"; +import { getNextListDepth } from "src/ui/extensions/custom-list-keymap/list-helpers/get-next-list-depth"; export const nextListIsDeeper = (typeOrName: string, state: EditorState) => { const listDepth = getNextListDepth(typeOrName, state); diff --git a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/next-list-is-higher.ts b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/next-list-is-higher.ts index 8b853b5af..3364c3b87 100644 --- a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/next-list-is-higher.ts +++ b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers/next-list-is-higher.ts @@ -1,7 +1,7 @@ import { EditorState } from "@tiptap/pm/state"; -import { findListItemPos } from "./find-list-item-pos"; -import { getNextListDepth } from "./get-next-list-depth"; +import { findListItemPos } from "src/ui/extensions/custom-list-keymap/list-helpers/find-list-item-pos"; +import { getNextListDepth } from "src/ui/extensions/custom-list-keymap/list-helpers/get-next-list-depth"; export const nextListIsHigher = (typeOrName: string, state: EditorState) => { const listDepth = getNextListDepth(typeOrName, state); diff --git a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-keymap.ts b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-keymap.ts index b61695973..aabd836d2 100644 --- a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-keymap.ts +++ b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-keymap.ts @@ -1,6 +1,6 @@ import { Extension } from "@tiptap/core"; -import { handleBackspace, handleDelete } from "./list-helpers"; +import { handleBackspace, handleDelete } from "src/ui/extensions/custom-list-keymap/list-helpers"; export type ListKeymapOptions = { listTypes: Array<{ diff --git a/packages/editor/core/src/ui/extensions/horizontal-rule.tsx b/packages/editor/core/src/ui/extensions/horizontal-rule.tsx index a7bbf50e1..cee0ded83 100644 --- a/packages/editor/core/src/ui/extensions/horizontal-rule.tsx +++ b/packages/editor/core/src/ui/extensions/horizontal-rule.tsx @@ -22,7 +22,7 @@ declare module "@tiptap/core" { } } -export default Node.create({ +export const HorizontalRule = Node.create({ name: "horizontalRule", addOptions() { diff --git a/packages/editor/core/src/ui/extensions/image/index.tsx b/packages/editor/core/src/ui/extensions/image/index.tsx index b11bfefce..db8b1c97b 100644 --- a/packages/editor/core/src/ui/extensions/image/index.tsx +++ b/packages/editor/core/src/ui/extensions/image/index.tsx @@ -1,9 +1,10 @@ import { EditorState, Plugin, PluginKey, Transaction } from "@tiptap/pm/state"; import { Node as ProseMirrorNode } from "@tiptap/pm/model"; -import UploadImagesPlugin from "../../plugins/upload-image"; +import { UploadImagesPlugin } from "src/ui/plugins/upload-image"; import ImageExt from "@tiptap/extension-image"; -import { onNodeDeleted, onNodeRestored } from "../../plugins/delete-image"; -import { DeleteImage, RestoreImage } from "@plane/editor-types"; +import { onNodeDeleted, onNodeRestored } from "src/ui/plugins/delete-image"; +import { DeleteImage } from "src/types/delete-image"; +import { RestoreImage } from "src/types/restore-image"; interface ImageNode extends ProseMirrorNode { attrs: { @@ -15,7 +16,7 @@ interface ImageNode extends ProseMirrorNode { const deleteKey = new PluginKey("delete-image"); const IMAGE_NODE_TYPE = "image"; -const ImageExtension = (deleteImage: DeleteImage, restoreFile: RestoreImage, cancelUploadImage?: () => any) => +export const ImageExtension = (deleteImage: DeleteImage, restoreFile: RestoreImage, cancelUploadImage?: () => any) => ImageExt.extend({ addProseMirrorPlugins() { return [ @@ -130,5 +131,3 @@ const ImageExtension = (deleteImage: DeleteImage, restoreFile: RestoreImage, can }; }, }); - -export default ImageExtension; diff --git a/packages/editor/core/src/ui/extensions/image/read-only-image.tsx b/packages/editor/core/src/ui/extensions/image/read-only-image.tsx index 73a763d04..8112eba4e 100644 --- a/packages/editor/core/src/ui/extensions/image/read-only-image.tsx +++ b/packages/editor/core/src/ui/extensions/image/read-only-image.tsx @@ -1,6 +1,6 @@ import Image from "@tiptap/extension-image"; -const ReadOnlyImageExtension = Image.extend({ +export const ReadOnlyImageExtension = Image.extend({ addAttributes() { return { ...this.parent?.(), @@ -13,5 +13,3 @@ const ReadOnlyImageExtension = Image.extend({ }; }, }); - -export default ReadOnlyImageExtension; diff --git a/packages/editor/core/src/ui/extensions/index.tsx b/packages/editor/core/src/ui/extensions/index.tsx index 4ab82f3c8..4ae55f00c 100644 --- a/packages/editor/core/src/ui/extensions/index.tsx +++ b/packages/editor/core/src/ui/extensions/index.tsx @@ -7,22 +7,25 @@ import TaskItem from "@tiptap/extension-task-item"; import TaskList from "@tiptap/extension-task-list"; import { Markdown } from "tiptap-markdown"; -import TableHeader from "./table/table-header/table-header"; -import Table from "./table/table"; -import TableCell from "./table/table-cell/table-cell"; -import TableRow from "./table/table-row/table-row"; -import HorizontalRule from "./horizontal-rule"; +import { TableHeader } from "src/ui/extensions/table/table-header/table-header"; +import { Table } from "src/ui/extensions/table/table"; +import { TableCell } from "src/ui/extensions/table/table-cell/table-cell"; +import { TableRow } from "src/ui/extensions/table/table-row/table-row"; +import { HorizontalRule } from "src/ui/extensions/horizontal-rule"; -import ImageExtension from "./image"; +import { ImageExtension } from "src/ui/extensions/image"; -import { isValidHttpUrl } from "../../lib/utils"; -import { Mentions } from "../mentions"; +import { isValidHttpUrl } from "src/lib/utils"; +import { Mentions } from "src/ui/mentions"; -import { CustomKeymap } from "./keymap"; -import { CustomCodeBlock } from "./code"; -import { CustomQuoteExtension } from "./quote"; -import { ListKeymap } from "./custom-list-keymap"; -import { IMentionSuggestion, DeleteImage, RestoreImage } from "@plane/editor-types"; +import { CustomKeymap } from "src/ui/extensions/keymap"; +import { CustomCodeBlock } from "src/ui/extensions/code"; +import { CustomQuoteExtension } from "src/ui/extensions/quote"; +import { ListKeymap } from "src/ui/extensions/custom-list-keymap"; + +import { DeleteImage } from "src/types/delete-image"; +import { IMentionSuggestion } from "src/types/mention-suggestion"; +import { RestoreImage } from "src/types/restore-image"; export const CoreEditorExtensions = ( mentionConfig: { @@ -49,12 +52,12 @@ export const CoreEditorExtensions = ( class: "leading-normal -mb-2", }, }, - // blockquote: { - // HTMLAttributes: { - // class: "border-l-4 border-custom-border-300", - // }, - // }, - code: false, + code: { + HTMLAttributes: { + class: "rounded-md bg-custom-primary-30 mx-1 px-1 py-1 font-mono font-medium text-custom-text-1000", + spellcheck: "false", + }, + }, codeBlock: false, horizontalRule: false, dropcursor: { diff --git a/packages/editor/core/src/ui/extensions/table/table-cell/index.ts b/packages/editor/core/src/ui/extensions/table/table-cell/index.ts index fb2183381..68a25a9c3 100644 --- a/packages/editor/core/src/ui/extensions/table/table-cell/index.ts +++ b/packages/editor/core/src/ui/extensions/table/table-cell/index.ts @@ -1 +1 @@ -export { default as default } from "./table-cell"; +export { TableCell } from "./table-cell"; diff --git a/packages/editor/core/src/ui/extensions/table/table-cell/table-cell.ts b/packages/editor/core/src/ui/extensions/table/table-cell/table-cell.ts index 1d3e57af9..aedb59411 100644 --- a/packages/editor/core/src/ui/extensions/table/table-cell/table-cell.ts +++ b/packages/editor/core/src/ui/extensions/table/table-cell/table-cell.ts @@ -4,7 +4,7 @@ export interface TableCellOptions { HTMLAttributes: Record; } -export default Node.create({ +export const TableCell = Node.create({ name: "tableCell", addOptions() { diff --git a/packages/editor/core/src/ui/extensions/table/table-header/index.ts b/packages/editor/core/src/ui/extensions/table/table-header/index.ts index cb036c505..290f37d0b 100644 --- a/packages/editor/core/src/ui/extensions/table/table-header/index.ts +++ b/packages/editor/core/src/ui/extensions/table/table-header/index.ts @@ -1 +1 @@ -export { default as default } from "./table-header"; +export { TableHeader } from "./table-header"; diff --git a/packages/editor/core/src/ui/extensions/table/table-header/table-header.ts b/packages/editor/core/src/ui/extensions/table/table-header/table-header.ts index 0148f1a6f..c0decdbf8 100644 --- a/packages/editor/core/src/ui/extensions/table/table-header/table-header.ts +++ b/packages/editor/core/src/ui/extensions/table/table-header/table-header.ts @@ -3,7 +3,8 @@ import { mergeAttributes, Node } from "@tiptap/core"; export interface TableHeaderOptions { HTMLAttributes: Record; } -export default Node.create({ + +export const TableHeader = Node.create({ name: "tableHeader", addOptions() { diff --git a/packages/editor/core/src/ui/extensions/table/table-row/index.ts b/packages/editor/core/src/ui/extensions/table/table-row/index.ts index 8c6eb55aa..24dafb7e0 100644 --- a/packages/editor/core/src/ui/extensions/table/table-row/index.ts +++ b/packages/editor/core/src/ui/extensions/table/table-row/index.ts @@ -1 +1 @@ -export { default as default } from "./table-row"; +export { TableRow } from "./table-row"; diff --git a/packages/editor/core/src/ui/extensions/table/table-row/table-row.ts b/packages/editor/core/src/ui/extensions/table/table-row/table-row.ts index 5df20e6ef..28c9a9a48 100644 --- a/packages/editor/core/src/ui/extensions/table/table-row/table-row.ts +++ b/packages/editor/core/src/ui/extensions/table/table-row/table-row.ts @@ -4,7 +4,7 @@ export interface TableRowOptions { HTMLAttributes: Record; } -export default Node.create({ +export const TableRow = Node.create({ name: "tableRow", addOptions() { diff --git a/packages/editor/core/src/ui/extensions/table/table/icons.ts b/packages/editor/core/src/ui/extensions/table/table/icons.ts index 65e8b8540..c08710ec3 100644 --- a/packages/editor/core/src/ui/extensions/table/table/icons.ts +++ b/packages/editor/core/src/ui/extensions/table/table/icons.ts @@ -1,4 +1,4 @@ -const icons = { +export const icons = { colorPicker: ``, deleteColumn: ``, deleteRow: ``, @@ -47,5 +47,3 @@ const icons = { `, }; - -export default icons; diff --git a/packages/editor/core/src/ui/extensions/table/table/index.ts b/packages/editor/core/src/ui/extensions/table/table/index.ts index ac51d0e2c..8efc43120 100644 --- a/packages/editor/core/src/ui/extensions/table/table/index.ts +++ b/packages/editor/core/src/ui/extensions/table/table/index.ts @@ -1 +1 @@ -export { default as default } from "./table"; +export { Table } from "./table"; diff --git a/packages/editor/core/src/ui/extensions/table/table/table-view.tsx b/packages/editor/core/src/ui/extensions/table/table/table-view.tsx index 5b0622243..bc42b49ff 100644 --- a/packages/editor/core/src/ui/extensions/table/table/table-view.tsx +++ b/packages/editor/core/src/ui/extensions/table/table/table-view.tsx @@ -4,9 +4,9 @@ import { Decoration, NodeView } from "@tiptap/pm/view"; import tippy, { Instance, Props } from "tippy.js"; import { Editor } from "@tiptap/core"; -import { CellSelection, TableMap, updateColumnsOnResize } from "@tiptap/prosemirror-tables"; +import { CellSelection, TableMap, updateColumnsOnResize } from "@tiptap/pm/tables"; -import icons from "./icons"; +import { icons } from "src/ui/extensions/table/table/icons"; export function updateColumns( node: ProseMirrorNode, diff --git a/packages/editor/core/src/ui/extensions/table/table/table.ts b/packages/editor/core/src/ui/extensions/table/table/table.ts index 71c75f616..5600fd82a 100644 --- a/packages/editor/core/src/ui/extensions/table/table/table.ts +++ b/packages/editor/core/src/ui/extensions/table/table/table.ts @@ -19,12 +19,12 @@ import { tableEditing, toggleHeader, toggleHeaderCell, -} from "@tiptap/prosemirror-tables"; +} from "@tiptap/pm/tables"; -import { tableControls } from "./table-controls"; -import { TableView } from "./table-view"; -import { createTable } from "./utilities/create-table"; -import { deleteTableWhenAllCellsSelected } from "./utilities/delete-table-when-all-cells-selected"; +import { tableControls } from "src/ui/extensions/table/table/table-controls"; +import { TableView } from "src/ui/extensions/table/table/table-view"; +import { createTable } from "src/ui/extensions/table/table/utilities/create-table"; +import { deleteTableWhenAllCellsSelected } from "src/ui/extensions/table/table/utilities/delete-table-when-all-cells-selected"; export interface TableOptions { HTMLAttributes: Record; @@ -72,7 +72,7 @@ declare module "@tiptap/core" { } } -export default Node.create({ +export const Table = Node.create({ name: "table", addOptions() { diff --git a/packages/editor/core/src/ui/extensions/table/table/utilities/create-table.ts b/packages/editor/core/src/ui/extensions/table/table/utilities/create-table.ts index 5a2299fb4..7299dd442 100644 --- a/packages/editor/core/src/ui/extensions/table/table/utilities/create-table.ts +++ b/packages/editor/core/src/ui/extensions/table/table/utilities/create-table.ts @@ -1,7 +1,7 @@ import { Fragment, Node as ProsemirrorNode, Schema } from "@tiptap/pm/model"; -import { createCell } from "./create-cell"; -import { getTableNodeTypes } from "./get-table-node-types"; +import { createCell } from "src/ui/extensions/table/table/utilities/create-cell"; +import { getTableNodeTypes } from "src/ui/extensions/table/table/utilities/get-table-node-types"; export function createTable( schema: Schema, diff --git a/packages/editor/core/src/ui/extensions/table/table/utilities/delete-table-when-all-cells-selected.ts b/packages/editor/core/src/ui/extensions/table/table/utilities/delete-table-when-all-cells-selected.ts index 7b5386382..c08228a00 100644 --- a/packages/editor/core/src/ui/extensions/table/table/utilities/delete-table-when-all-cells-selected.ts +++ b/packages/editor/core/src/ui/extensions/table/table/utilities/delete-table-when-all-cells-selected.ts @@ -1,6 +1,6 @@ import { findParentNodeClosestToPos, KeyboardShortcutCommand } from "@tiptap/core"; -import { isCellSelection } from "./is-cell-selection"; +import { isCellSelection } from "src/ui/extensions/table/table/utilities/is-cell-selection"; export const deleteTableWhenAllCellsSelected: KeyboardShortcutCommand = ({ editor }) => { const { selection } = editor.state; diff --git a/packages/editor/core/src/ui/extensions/table/table/utilities/is-cell-selection.ts b/packages/editor/core/src/ui/extensions/table/table/utilities/is-cell-selection.ts index 28917a299..42ea5759c 100644 --- a/packages/editor/core/src/ui/extensions/table/table/utilities/is-cell-selection.ts +++ b/packages/editor/core/src/ui/extensions/table/table/utilities/is-cell-selection.ts @@ -1,4 +1,4 @@ -import { CellSelection } from "@tiptap/prosemirror-tables"; +import { CellSelection } from "@tiptap/pm/tables"; export function isCellSelection(value: unknown): value is CellSelection { return value instanceof CellSelection; diff --git a/packages/editor/core/src/ui/mentions/custom.tsx b/packages/editor/core/src/ui/mentions/custom.tsx index e25da6f47..6a47d79f0 100644 --- a/packages/editor/core/src/ui/mentions/custom.tsx +++ b/packages/editor/core/src/ui/mentions/custom.tsx @@ -1,8 +1,8 @@ import { Mention, MentionOptions } from "@tiptap/extension-mention"; import { mergeAttributes } from "@tiptap/core"; import { ReactNodeViewRenderer } from "@tiptap/react"; -import mentionNodeView from "./mentionNodeView"; -import { IMentionHighlight } from "@plane/editor-types"; +import { MentionNodeView } from "src/ui/mentions/mention-node-view"; +import { IMentionHighlight } from "src/types/mention-suggestion"; export interface CustomMentionOptions extends MentionOptions { mentionHighlights: IMentionHighlight[]; @@ -31,7 +31,7 @@ export const CustomMention = Mention.extend({ }, addNodeView() { - return ReactNodeViewRenderer(mentionNodeView); + return ReactNodeViewRenderer(MentionNodeView); }, parseHTML() { diff --git a/packages/editor/core/src/ui/mentions/index.tsx b/packages/editor/core/src/ui/mentions/index.tsx index b78923f6c..f6d3e5b1f 100644 --- a/packages/editor/core/src/ui/mentions/index.tsx +++ b/packages/editor/core/src/ui/mentions/index.tsx @@ -1,8 +1,8 @@ // @ts-nocheck -import suggestion from "./suggestion"; -import { CustomMention } from "./custom"; -import { IMentionHighlight, IMentionSuggestion } from "@plane/editor-types"; +import { Suggestion } from "src/ui/mentions/suggestion"; +import { CustomMention } from "src/ui/mentions/custom"; +import { IMentionHighlight } from "src/types/mention-suggestion"; export const Mentions = (mentionSuggestions: IMentionSuggestion[], mentionHighlights: IMentionHighlight[], readonly) => CustomMention.configure({ @@ -11,5 +11,5 @@ export const Mentions = (mentionSuggestions: IMentionSuggestion[], mentionHighli }, readonly: readonly, mentionHighlights: mentionHighlights, - suggestion: suggestion(mentionSuggestions), + suggestion: Suggestion(mentionSuggestions), }); diff --git a/packages/editor/core/src/ui/mentions/MentionList.tsx b/packages/editor/core/src/ui/mentions/mention-list.tsx similarity index 91% rename from packages/editor/core/src/ui/mentions/MentionList.tsx rename to packages/editor/core/src/ui/mentions/mention-list.tsx index d103a9e0a..afbf10970 100644 --- a/packages/editor/core/src/ui/mentions/MentionList.tsx +++ b/packages/editor/core/src/ui/mentions/mention-list.tsx @@ -1,6 +1,6 @@ -import { IMentionSuggestion } from "@plane/editor-types"; import { Editor } from "@tiptap/react"; -import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from "react"; +import { forwardRef, useEffect, useImperativeHandle, useState } from "react"; +import { IMentionSuggestion } from "src/types/mention-suggestion"; interface MentionListProps { items: IMentionSuggestion[]; @@ -9,7 +9,7 @@ interface MentionListProps { } // eslint-disable-next-line react/display-name -const MentionList = forwardRef((props: MentionListProps, ref) => { +export const MentionList = forwardRef((props: MentionListProps, ref) => { const [selectedIndex, setSelectedIndex] = useState(0); const selectItem = (index: number) => { @@ -98,5 +98,3 @@ const MentionList = forwardRef((props: MentionListProps, ref) => { }); MentionList.displayName = "MentionList"; - -export default MentionList; diff --git a/packages/editor/core/src/ui/mentions/mentionNodeView.tsx b/packages/editor/core/src/ui/mentions/mention-node-view.tsx similarity index 89% rename from packages/editor/core/src/ui/mentions/mentionNodeView.tsx rename to packages/editor/core/src/ui/mentions/mention-node-view.tsx index 8e9672d9f..1c3755f6c 100644 --- a/packages/editor/core/src/ui/mentions/mentionNodeView.tsx +++ b/packages/editor/core/src/ui/mentions/mention-node-view.tsx @@ -1,12 +1,12 @@ /* eslint-disable react/display-name */ // @ts-nocheck import { NodeViewWrapper } from "@tiptap/react"; -import { cn } from "../../lib/utils"; +import { cn } from "src/lib/utils"; import { useRouter } from "next/router"; -import { IMentionHighlight } from "@plane/editor-types"; +import { IMentionHighlight } from "src/types/mention-suggestion"; // eslint-disable-next-line import/no-anonymous-default-export -export default (props) => { +export const MentionNodeView = (props) => { const router = useRouter(); const highlights = props.extension.options.mentionHighlights as IMentionHighlight[]; diff --git a/packages/editor/core/src/ui/mentions/suggestion.ts b/packages/editor/core/src/ui/mentions/suggestion.ts index 60ba6f4bc..6d706cb79 100644 --- a/packages/editor/core/src/ui/mentions/suggestion.ts +++ b/packages/editor/core/src/ui/mentions/suggestion.ts @@ -2,10 +2,10 @@ import { ReactRenderer } from "@tiptap/react"; import { Editor } from "@tiptap/core"; import tippy from "tippy.js"; -import MentionList from "./MentionList"; -import { IMentionSuggestion } from "@plane/editor-types"; +import { MentionList } from "src/ui/mentions/mention-list"; +import { IMentionSuggestion } from "src/types/mention-suggestion"; -const Suggestion = (suggestions: IMentionSuggestion[]) => ({ +export const Suggestion = (suggestions: IMentionSuggestion[]) => ({ items: ({ query }: { query: string }) => suggestions.filter((suggestion) => suggestion.title.toLowerCase().startsWith(query.toLowerCase())).slice(0, 5), render: () => { @@ -55,5 +55,3 @@ const Suggestion = (suggestions: IMentionSuggestion[]) => ({ }; }, }); - -export default Suggestion; diff --git a/packages/editor/core/src/ui/menus/menu-items/index.tsx b/packages/editor/core/src/ui/menus/menu-items/index.tsx index 98069b694..610d677f8 100644 --- a/packages/editor/core/src/ui/menus/menu-items/index.tsx +++ b/packages/editor/core/src/ui/menus/menu-items/index.tsx @@ -30,14 +30,15 @@ import { toggleStrike, toggleTaskList, toggleUnderline, -} from "../../../lib/editor-commands"; -import { UploadImage } from "@plane/editor-types"; +} from "src/lib/editor-commands"; +import { LucideIconType } from "src/types/lucide-icon"; +import { UploadImage } from "src/types/upload-image"; export interface EditorMenuItem { name: string; isActive: () => boolean; command: () => void; - icon: typeof BoldIcon; + icon: LucideIconType; } export const HeadingOneItem = (editor: Editor): EditorMenuItem => ({ diff --git a/packages/editor/core/src/ui/plugins/delete-image.tsx b/packages/editor/core/src/ui/plugins/delete-image.tsx index 6b772cebf..afe13730a 100644 --- a/packages/editor/core/src/ui/plugins/delete-image.tsx +++ b/packages/editor/core/src/ui/plugins/delete-image.tsx @@ -1,6 +1,7 @@ import { EditorState, Plugin, PluginKey, Transaction } from "@tiptap/pm/state"; import { Node as ProseMirrorNode } from "@tiptap/pm/model"; -import { DeleteImage, RestoreImage } from "@plane/editor-types"; +import { DeleteImage } from "src/types/delete-image"; +import { RestoreImage } from "src/types/restore-image"; const deleteKey = new PluginKey("delete-image"); const IMAGE_NODE_TYPE = "image"; @@ -12,7 +13,7 @@ interface ImageNode extends ProseMirrorNode { }; } -const TrackImageDeletionPlugin = (deleteImage: DeleteImage): Plugin => +export const TrackImageDeletionPlugin = (deleteImage: DeleteImage): Plugin => new Plugin({ key: deleteKey, appendTransaction: (transactions: readonly Transaction[], oldState: EditorState, newState: EditorState) => { @@ -53,8 +54,6 @@ const TrackImageDeletionPlugin = (deleteImage: DeleteImage): Plugin => }, }); -export default TrackImageDeletionPlugin; - export async function onNodeDeleted(src: string, deleteImage: DeleteImage): Promise { try { const assetUrlWithWorkspaceId = new URL(src).pathname.substring(1); diff --git a/packages/editor/core/src/ui/plugins/upload-image.tsx b/packages/editor/core/src/ui/plugins/upload-image.tsx index 4dee70da4..738653d71 100644 --- a/packages/editor/core/src/ui/plugins/upload-image.tsx +++ b/packages/editor/core/src/ui/plugins/upload-image.tsx @@ -1,10 +1,10 @@ -import { UploadImage } from "@plane/editor-types"; import { EditorState, Plugin, PluginKey } from "@tiptap/pm/state"; import { Decoration, DecorationSet, EditorView } from "@tiptap/pm/view"; +import { UploadImage } from "src/types/upload-image"; const uploadKey = new PluginKey("upload-image"); -const UploadImagesPlugin = (cancelUploadImage?: () => any) => +export const UploadImagesPlugin = (cancelUploadImage?: () => any) => new Plugin({ key: uploadKey, state: { @@ -43,7 +43,7 @@ const UploadImagesPlugin = (cancelUploadImage?: () => any) => cancelButton.appendChild(svgElement); placeholder.appendChild(cancelButton); - const deco = Decoration.widget(pos + 1, placeholder, { + const deco = Decoration.widget(pos, placeholder, { id, }); set = set.add(tr.doc, [deco]); @@ -60,8 +60,6 @@ const UploadImagesPlugin = (cancelUploadImage?: () => any) => }, }); -export default UploadImagesPlugin; - function findPlaceholder(state: EditorState, id: {}) { const decos = uploadKey.getState(state); const found = decos.find(undefined, undefined, (spec: { id: number | undefined }) => spec.id == id); @@ -133,7 +131,8 @@ export async function startImageUpload( const imageSrc = typeof src === "object" ? reader.result : src; const node = schema.nodes.image.create({ src: imageSrc }); - const transaction = view.state.tr.replaceWith(pos, pos, node).setMeta(uploadKey, { remove: { id } }); + const transaction = view.state.tr.insert(pos - 1, node).setMeta(uploadKey, { remove: { id } }); + view.dispatch(transaction); } catch (error) { console.error("Upload error: ", error); diff --git a/packages/editor/core/src/ui/props.tsx b/packages/editor/core/src/ui/props.tsx index edd070d7b..2aaeb4264 100644 --- a/packages/editor/core/src/ui/props.tsx +++ b/packages/editor/core/src/ui/props.tsx @@ -1,7 +1,7 @@ -import { UploadImage } from "@plane/editor-types"; import { EditorProps } from "@tiptap/pm/view"; -import { findTableAncestor } from "../lib/utils"; -import { startImageUpload } from "./plugins/upload-image"; +import { findTableAncestor } from "src/lib/utils"; +import { UploadImage } from "src/types/upload-image"; +import { startImageUpload } from "src/ui/plugins/upload-image"; export function CoreEditorProps( uploadFile: UploadImage, diff --git a/packages/editor/core/src/ui/read-only/extensions.tsx b/packages/editor/core/src/ui/read-only/extensions.tsx index cdf7f88e5..5795d6c4a 100644 --- a/packages/editor/core/src/ui/read-only/extensions.tsx +++ b/packages/editor/core/src/ui/read-only/extensions.tsx @@ -8,15 +8,16 @@ import TaskList from "@tiptap/extension-task-list"; import { Markdown } from "tiptap-markdown"; import Gapcursor from "@tiptap/extension-gapcursor"; -import TableHeader from "../extensions/table/table-header/table-header"; -import Table from "../extensions/table/table"; -import TableCell from "../extensions/table/table-cell/table-cell"; -import TableRow from "../extensions/table/table-row/table-row"; +import { TableHeader } from "src/ui/extensions/table/table-header/table-header"; +import { Table } from "src/ui/extensions/table/table"; +import { TableCell } from "src/ui/extensions/table/table-cell/table-cell"; +import { TableRow } from "src/ui/extensions/table/table-row/table-row"; +import { HorizontalRule } from "src/ui/extensions/horizontal-rule"; -import ReadOnlyImageExtension from "../extensions/image/read-only-image"; -import { isValidHttpUrl } from "../../lib/utils"; -import { Mentions } from "../mentions"; -import { IMentionSuggestion } from "@plane/editor-types"; +import { ReadOnlyImageExtension } from "src/ui/extensions/image/read-only-image"; +import { isValidHttpUrl } from "src/lib/utils"; +import { Mentions } from "src/ui/mentions"; +import { IMentionSuggestion } from "src/types/mention-suggestion"; export const CoreReadOnlyEditorExtensions = (mentionConfig: { mentionSuggestions: IMentionSuggestion[]; @@ -71,6 +72,7 @@ export const CoreReadOnlyEditorExtensions = (mentionConfig: { class: "rounded-lg border border-custom-border-300", }, }), + HorizontalRule, TiptapUnderline, TextStyle, Color, diff --git a/packages/editor/core/tsconfig.json b/packages/editor/core/tsconfig.json index 57d0e9a74..c15534037 100644 --- a/packages/editor/core/tsconfig.json +++ b/packages/editor/core/tsconfig.json @@ -1,5 +1,15 @@ { "extends": "tsconfig/react-library.json", - "include": ["src/**/*", "index.d.ts"], - "exclude": ["dist", "build", "node_modules"] + "include": [ + "src/**/*", + "index.d.ts" + ], + "exclude": [ + "dist", + "build", + "node_modules" + ], + "compilerOptions": { + "baseUrl": "." + } } diff --git a/packages/editor/document-editor/package.json b/packages/editor/document-editor/package.json index 737a0eae0..72dfab954 100644 --- a/packages/editor/document-editor/package.json +++ b/packages/editor/document-editor/package.json @@ -30,13 +30,11 @@ "dependencies": { "@plane/editor-core": "*", "@plane/editor-extensions": "*", - "@plane/editor-types": "*", "@plane/ui": "*", - "@tiptap/core": "^2.1.7", - "@tiptap/extension-placeholder": "^2.1.11", - "@tiptap/pm": "^2.1.12", - "@tiptap/suggestion": "^2.1.12", - "eslint": "8.36.0", + "@tiptap/core": "^2.1.13", + "@tiptap/extension-placeholder": "^2.1.13", + "@tiptap/pm": "^2.1.13", + "@tiptap/suggestion": "^2.1.13", "eslint-config-next": "13.2.4", "react-popper": "^2.3.0", "tippy.js": "^6.3.7", @@ -46,7 +44,7 @@ "@types/node": "18.15.3", "@types/react": "^18.2.42", "@types/react-dom": "^18.2.17", - "eslint": "^7.32.0", + "eslint": "8.36.0", "postcss": "^8.4.29", "tailwind-config-custom": "*", "tsconfig": "*", diff --git a/packages/editor/document-editor/src/ui/hooks/use-editor-markings.tsx b/packages/editor/document-editor/src/hooks/use-editor-markings.tsx similarity index 93% rename from packages/editor/document-editor/src/ui/hooks/use-editor-markings.tsx rename to packages/editor/document-editor/src/hooks/use-editor-markings.tsx index 9dfef6c39..1eb72eaab 100644 --- a/packages/editor/document-editor/src/ui/hooks/use-editor-markings.tsx +++ b/packages/editor/document-editor/src/hooks/use-editor-markings.tsx @@ -1,6 +1,5 @@ -import { Editor } from "@tiptap/react"; import { useState } from "react"; -import { IMarking } from ".."; +import { IMarking } from "src/types/editor-types"; export const useEditorMarkings = () => { const [markings, setMarkings] = useState([]); diff --git a/packages/editor/document-editor/src/index.ts b/packages/editor/document-editor/src/index.ts index 356e1faf9..c074009f4 100644 --- a/packages/editor/document-editor/src/index.ts +++ b/packages/editor/document-editor/src/index.ts @@ -1,3 +1,3 @@ -export { DocumentEditor, DocumentEditorWithRef } from "./ui"; -export { DocumentReadOnlyEditor, DocumentReadOnlyEditorWithRef } from "./ui/readonly"; -export { FixedMenu } from "./ui/menu/fixed-menu"; +export { DocumentEditor, DocumentEditorWithRef } from "src/ui"; +export { DocumentReadOnlyEditor, DocumentReadOnlyEditorWithRef } from "src/ui/readonly"; +export { FixedMenu } from "src/ui/menu/fixed-menu"; diff --git a/packages/editor/document-editor/src/ui/types/editor-types.ts b/packages/editor/document-editor/src/types/editor-types.ts similarity index 59% rename from packages/editor/document-editor/src/ui/types/editor-types.ts rename to packages/editor/document-editor/src/types/editor-types.ts index 10e9b16b6..5a28daf9e 100644 --- a/packages/editor/document-editor/src/ui/types/editor-types.ts +++ b/packages/editor/document-editor/src/types/editor-types.ts @@ -5,3 +5,9 @@ export interface DocumentDetails { last_updated_by: string; last_updated_at: Date; } +export interface IMarking { + type: "heading"; + level: number; + text: string; + sequence: number; +} diff --git a/packages/editor/document-editor/src/types/mark.ts b/packages/editor/document-editor/src/types/mark.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor/document-editor/src/ui/types/menu-actions.d.ts b/packages/editor/document-editor/src/types/menu-actions.d.ts similarity index 100% rename from packages/editor/document-editor/src/ui/types/menu-actions.d.ts rename to packages/editor/document-editor/src/types/menu-actions.d.ts diff --git a/packages/editor/document-editor/src/ui/components/alert-label.tsx b/packages/editor/document-editor/src/ui/components/alert-label.tsx index 395ea2317..69b6dd02d 100644 --- a/packages/editor/document-editor/src/ui/components/alert-label.tsx +++ b/packages/editor/document-editor/src/ui/components/alert-label.tsx @@ -1,12 +1,11 @@ -import { Icon } from "lucide-react"; +import { LucideIconType } from "@plane/editor-core"; interface IAlertLabelProps { - Icon?: Icon; + Icon?: LucideIconType; backgroundColor: string; textColor?: string; label: string; } - export const AlertLabel = (props: IAlertLabelProps) => { const { Icon, backgroundColor, textColor, label } = props; diff --git a/packages/editor/document-editor/src/ui/components/content-browser.tsx b/packages/editor/document-editor/src/ui/components/content-browser.tsx index a21ca268f..18a50a5a8 100644 --- a/packages/editor/document-editor/src/ui/components/content-browser.tsx +++ b/packages/editor/document-editor/src/ui/components/content-browser.tsx @@ -1,7 +1,7 @@ -import { HeadingComp, HeadingThreeComp, SubheadingComp } from "./heading-component"; -import { IMarking } from ".."; +import { HeadingComp, HeadingThreeComp, SubheadingComp } from "src/ui/components/heading-component"; +import { IMarking } from "src/types/editor-types"; import { Editor } from "@tiptap/react"; -import { scrollSummary } from "../utils/editor-summary-utils"; +import { scrollSummary } from "src/utils/editor-summary-utils"; interface ContentBrowserProps { editor: Editor; diff --git a/packages/editor/document-editor/src/ui/components/editor-header.tsx b/packages/editor/document-editor/src/ui/components/editor-header.tsx index 7e2167ba0..3501785a7 100644 --- a/packages/editor/document-editor/src/ui/components/editor-header.tsx +++ b/packages/editor/document-editor/src/ui/components/editor-header.tsx @@ -1,13 +1,12 @@ import { Editor } from "@tiptap/react"; import { Archive, RefreshCw, Lock } from "lucide-react"; -import { IMarking } from ".."; -import { FixedMenu } from "../menu"; -import { UploadImage } from "@plane/editor-types"; -import { DocumentDetails } from "../types/editor-types"; -import { AlertLabel } from "./alert-label"; -import { IVerticalDropdownItemProps, VerticalDropdownMenu } from "./vertical-dropdown-menu"; -import { SummaryPopover } from "./summary-popover"; -import { InfoPopover } from "./info-popover"; +import { IMarking, DocumentDetails } from "src/types/editor-types"; +import { FixedMenu } from "src/ui/menu"; +import { UploadImage } from "@plane/editor-core"; +import { AlertLabel } from "src/ui/components/alert-label"; +import { IVerticalDropdownItemProps, VerticalDropdownMenu } from "src/ui/components/vertical-dropdown-menu"; +import { SummaryPopover } from "src/ui/components/summary-popover"; +import { InfoPopover } from "src/ui/components/info-popover"; interface IEditorHeader { editor: Editor; diff --git a/packages/editor/document-editor/src/ui/components/info-popover.tsx b/packages/editor/document-editor/src/ui/components/info-popover.tsx index 0d650667e..f78dd3473 100644 --- a/packages/editor/document-editor/src/ui/components/info-popover.tsx +++ b/packages/editor/document-editor/src/ui/components/info-popover.tsx @@ -2,7 +2,7 @@ import { useState } from "react"; import { usePopper } from "react-popper"; import { Calendar, History, Info } from "lucide-react"; // types -import { DocumentDetails } from "../types/editor-types"; +import { DocumentDetails } from "src/types/editor-types"; type Props = { documentDetails: DocumentDetails; diff --git a/packages/editor/document-editor/src/ui/components/page-renderer.tsx b/packages/editor/document-editor/src/ui/components/page-renderer.tsx index d25e9ca43..c2d001abe 100644 --- a/packages/editor/document-editor/src/ui/components/page-renderer.tsx +++ b/packages/editor/document-editor/src/ui/components/page-renderer.tsx @@ -1,7 +1,7 @@ import { EditorContainer, EditorContentWrapper } from "@plane/editor-core"; import { Editor } from "@tiptap/react"; import { useState } from "react"; -import { DocumentDetails } from "../types/editor-types"; +import { DocumentDetails } from "src/types/editor-types"; type IPageRenderer = { documentDetails: DocumentDetails; diff --git a/packages/editor/document-editor/src/ui/components/summary-popover.tsx b/packages/editor/document-editor/src/ui/components/summary-popover.tsx index 61361c049..d3ec64f1c 100644 --- a/packages/editor/document-editor/src/ui/components/summary-popover.tsx +++ b/packages/editor/document-editor/src/ui/components/summary-popover.tsx @@ -3,9 +3,9 @@ import { Editor } from "@tiptap/react"; import { usePopper } from "react-popper"; import { List } from "lucide-react"; // components -import { ContentBrowser } from "./content-browser"; +import { ContentBrowser } from "src/ui/components/content-browser"; // types -import { IMarking } from ".."; +import { IMarking } from "src/types/editor-types"; type Props = { editor: Editor; diff --git a/packages/editor/document-editor/src/ui/components/summary-side-bar.tsx b/packages/editor/document-editor/src/ui/components/summary-side-bar.tsx index b3bf23464..44ede3e8d 100644 --- a/packages/editor/document-editor/src/ui/components/summary-side-bar.tsx +++ b/packages/editor/document-editor/src/ui/components/summary-side-bar.tsx @@ -1,6 +1,6 @@ import { Editor } from "@tiptap/react"; -import { IMarking } from ".."; -import { ContentBrowser } from "./content-browser"; +import { IMarking } from "src/types/editor-types"; +import { ContentBrowser } from "src/ui/components/content-browser"; interface ISummarySideBarProps { editor: Editor; @@ -8,14 +8,12 @@ interface ISummarySideBarProps { sidePeekVisible: boolean; } -export const SummarySideBar = ({ editor, markings, sidePeekVisible }: ISummarySideBarProps) => { - return ( -
- -
- ); -}; +export const SummarySideBar = ({ editor, markings, sidePeekVisible }: ISummarySideBarProps) => ( +
+ +
+); diff --git a/packages/editor/document-editor/src/ui/components/vertical-dropdown-menu.tsx b/packages/editor/document-editor/src/ui/components/vertical-dropdown-menu.tsx index 93fea4730..43843e507 100644 --- a/packages/editor/document-editor/src/ui/components/vertical-dropdown-menu.tsx +++ b/packages/editor/document-editor/src/ui/components/vertical-dropdown-menu.tsx @@ -1,5 +1,6 @@ -import { Button, CustomMenu } from "@plane/ui"; -import { ChevronUp, Icon, MoreVertical } from "lucide-react"; +import { LucideIconType } from "@plane/editor-core"; +import { CustomMenu } from "@plane/ui"; +import { MoreVertical } from "lucide-react"; type TMenuItems = | "archive_page" @@ -14,7 +15,7 @@ type TMenuItems = export interface IVerticalDropdownItemProps { key: number; type: TMenuItems; - Icon: Icon; + Icon: LucideIconType; label: string; action: () => Promise | void; } @@ -23,27 +24,23 @@ export interface IVerticalDropdownMenuProps { items: IVerticalDropdownItemProps[]; } -const VerticalDropdownItem = ({ Icon, label, action }: IVerticalDropdownItemProps) => { - return ( - - -
{label}
-
- ); -}; +const VerticalDropdownItem = ({ Icon, label, action }: IVerticalDropdownItemProps) => ( + + +
{label}
+
+); -export const VerticalDropdownMenu = ({ items }: IVerticalDropdownMenuProps) => { - return ( - } - > - {items.map((item, index) => ( - - ))} - - ); -}; +export const VerticalDropdownMenu = ({ items }: IVerticalDropdownMenuProps) => ( + } + > + {items.map((item) => ( + + ))} + +); diff --git a/packages/editor/document-editor/src/ui/extensions/index.tsx b/packages/editor/document-editor/src/ui/extensions/index.tsx index 968328a76..155245f9e 100644 --- a/packages/editor/document-editor/src/ui/extensions/index.tsx +++ b/packages/editor/document-editor/src/ui/extensions/index.tsx @@ -1,11 +1,11 @@ import Placeholder from "@tiptap/extension-placeholder"; -import { IssueWidgetExtension } from "./widgets/IssueEmbedWidget"; +import { IssueWidgetExtension } from "src/ui/extensions/widgets/issue-embed-widget"; -import { IIssueEmbedConfig } from "./widgets/IssueEmbedWidget/types"; +import { IIssueEmbedConfig } from "src/ui/extensions/widgets/issue-embed-widget/types"; import { SlashCommand, DragAndDrop } from "@plane/editor-extensions"; -import { ISlashCommandItem, UploadImage } from "@plane/editor-types"; -import { IssueSuggestions } from "./widgets/IssueEmbedSuggestionList"; +import { ISlashCommandItem, UploadImage } from "@plane/editor-core"; +import { IssueSuggestions } from "src/ui/extensions/widgets/issue-embed-suggestion-list"; import { LayersIcon } from "@plane/ui"; export const DocumentEditorExtensions = ( diff --git a/packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedSuggestionList/issue-suggestion-items.tsx b/packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedSuggestionList/issue-suggestion-items.tsx deleted file mode 100644 index b1f27ece3..000000000 --- a/packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedSuggestionList/issue-suggestion-items.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { IIssueListSuggestion } from "."; - -export const getIssueSuggestionItems = (issueSuggestions: Array) => { - return ({ query }: { query: string }) => { - const search = query.toLowerCase(); - const filteredSuggestions = issueSuggestions.filter((item) => { - return ( - item.title.toLowerCase().includes(search) || - item.identifier.toLowerCase().includes(search) || - item.priority.toLowerCase().includes(search) - ); - }); - - return filteredSuggestions; - }; -}; diff --git a/packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedSuggestionList/index.tsx b/packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-suggestion-list/index.tsx similarity index 79% rename from packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedSuggestionList/index.tsx rename to packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-suggestion-list/index.tsx index 07a10031d..acc6213c2 100644 --- a/packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedSuggestionList/index.tsx +++ b/packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-suggestion-list/index.tsx @@ -1,7 +1,7 @@ import { Editor, Range } from "@tiptap/react"; -import { IssueEmbedSuggestions } from "./issue-suggestion-extension"; -import { getIssueSuggestionItems } from "./issue-suggestion-items"; -import { IssueListRenderer } from "./issue-suggestion-renderer"; +import { IssueEmbedSuggestions } from "src/ui/extensions/widgets/issue-embed-suggestion-list/issue-suggestion-extension"; +import { getIssueSuggestionItems } from "src/ui/extensions/widgets/issue-embed-suggestion-list/issue-suggestion-items"; +import { IssueListRenderer } from "src/ui/extensions/widgets/issue-embed-suggestion-list/issue-suggestion-renderer"; import { v4 as uuidv4 } from "uuid"; export type CommandProps = { @@ -19,7 +19,7 @@ export interface IIssueListSuggestion { export const IssueSuggestions = (suggestions: any[]) => { const mappedSuggestions: IIssueListSuggestion[] = suggestions.map((suggestion): IIssueListSuggestion => { - let transactionId = uuidv4(); + const transactionId = uuidv4(); return { title: suggestion.name, priority: suggestion.priority.toString(), diff --git a/packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedSuggestionList/issue-suggestion-extension.tsx b/packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-suggestion-list/issue-suggestion-extension.tsx similarity index 100% rename from packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedSuggestionList/issue-suggestion-extension.tsx rename to packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-suggestion-list/issue-suggestion-extension.tsx diff --git a/packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-suggestion-list/issue-suggestion-items.tsx b/packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-suggestion-list/issue-suggestion-items.tsx new file mode 100644 index 000000000..df468f2ee --- /dev/null +++ b/packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-suggestion-list/issue-suggestion-items.tsx @@ -0,0 +1,15 @@ +import { IIssueListSuggestion } from "src/ui/extensions/widgets/issue-embed-suggestion-list"; + +export const getIssueSuggestionItems = + (issueSuggestions: Array) => + ({ query }: { query: string }) => { + const search = query.toLowerCase(); + const filteredSuggestions = issueSuggestions.filter( + (item) => + item.title.toLowerCase().includes(search) || + item.identifier.toLowerCase().includes(search) || + item.priority.toLowerCase().includes(search) + ); + + return filteredSuggestions; + }; diff --git a/packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedSuggestionList/issue-suggestion-renderer.tsx b/packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-suggestion-list/issue-suggestion-renderer.tsx similarity index 99% rename from packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedSuggestionList/issue-suggestion-renderer.tsx rename to packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-suggestion-list/issue-suggestion-renderer.tsx index 487d4f075..0a166c3e3 100644 --- a/packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedSuggestionList/issue-suggestion-renderer.tsx +++ b/packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-suggestion-list/issue-suggestion-renderer.tsx @@ -171,7 +171,7 @@ const IssueSuggestionList = ({ section === currentSection && index === selectedIndex, } )} - key={index} + key={item.identifier} onClick={() => selectItem(index)} >
{item.identifier}
diff --git a/packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedWidget/index.tsx b/packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-widget/index.tsx similarity index 55% rename from packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedWidget/index.tsx rename to packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-widget/index.tsx index fb521efef..9bbb34aa5 100644 --- a/packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedWidget/index.tsx +++ b/packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-widget/index.tsx @@ -1,5 +1,5 @@ -import { IssueWidget } from "./issue-widget-node"; -import { IIssueEmbedConfig } from "./types"; +import { IssueWidget } from "src/ui/extensions/widgets/issue-embed-widget/issue-widget-node"; +import { IIssueEmbedConfig } from "src/ui/extensions/widgets/issue-embed-widget/types"; interface IssueWidgetExtensionProps { issueEmbedConfig?: IIssueEmbedConfig; diff --git a/packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedWidget/issue-widget-card.tsx b/packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-widget/issue-widget-card.tsx similarity index 91% rename from packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedWidget/issue-widget-card.tsx rename to packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-widget/issue-widget-card.tsx index 18dad8cae..78554c26d 100644 --- a/packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedWidget/issue-widget-card.tsx +++ b/packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-widget/issue-widget-card.tsx @@ -4,7 +4,7 @@ import { NodeViewWrapper } from "@tiptap/react"; import { Avatar, AvatarGroup, Loader, PriorityIcon } from "@plane/ui"; import { Calendar, AlertTriangle } from "lucide-react"; -const IssueWidgetCard = (props) => { +export const IssueWidgetCard = (props) => { const [loading, setLoading] = useState(1); const [issueDetails, setIssueDetails] = useState(); @@ -42,11 +42,9 @@ const IssueWidgetCard = (props) => {
- {issueDetails.assignee_details.map((assignee) => { - return ( - - ); - })} + {issueDetails.assignee_details.map((assignee) => ( + + ))}
{issueDetails.target_date && ( @@ -76,5 +74,3 @@ const IssueWidgetCard = (props) => { ); }; - -export default IssueWidgetCard; diff --git a/packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedWidget/issue-widget-node.tsx b/packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-widget/issue-widget-node.tsx similarity index 94% rename from packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedWidget/issue-widget-node.tsx rename to packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-widget/issue-widget-node.tsx index c30fe5e5b..c13637bd9 100644 --- a/packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedWidget/issue-widget-node.tsx +++ b/packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-widget/issue-widget-node.tsx @@ -1,5 +1,5 @@ import { mergeAttributes, Node } from "@tiptap/core"; -import IssueWidgetCard from "./issue-widget-card"; +import { IssueWidgetCard } from "src/ui/extensions/widgets/issue-embed-widget/issue-widget-card"; import { ReactNodeViewRenderer } from "@tiptap/react"; export const IssueWidget = Node.create({ diff --git a/packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedWidget/types.ts b/packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-widget/types.ts similarity index 100% rename from packages/editor/document-editor/src/ui/extensions/widgets/IssueEmbedWidget/types.ts rename to packages/editor/document-editor/src/ui/extensions/widgets/issue-embed-widget/types.ts diff --git a/packages/editor/document-editor/src/ui/index.tsx b/packages/editor/document-editor/src/ui/index.tsx index a99d1e6a8..df3554024 100644 --- a/packages/editor/document-editor/src/ui/index.tsx +++ b/packages/editor/document-editor/src/ui/index.tsx @@ -1,17 +1,16 @@ "use client"; import React, { useState } from "react"; -import { getEditorClassNames, useEditor } from "@plane/editor-core"; -import { DocumentEditorExtensions } from "./extensions"; -import { IDuplicationConfig, IPageArchiveConfig, IPageLockConfig } from "./types/menu-actions"; -import { EditorHeader } from "./components/editor-header"; -import { useEditorMarkings } from "./hooks/use-editor-markings"; -import { SummarySideBar } from "./components/summary-side-bar"; -import { DocumentDetails } from "./types/editor-types"; -import { PageRenderer } from "./components/page-renderer"; -import { getMenuOptions } from "./utils/menu-options"; +import { UploadImage, DeleteImage, RestoreImage, getEditorClassNames, useEditor } from "@plane/editor-core"; +import { DocumentEditorExtensions } from "src/ui/extensions"; +import { IDuplicationConfig, IPageArchiveConfig, IPageLockConfig } from "src/types/menu-actions"; +import { EditorHeader } from "src/ui/components/editor-header"; +import { useEditorMarkings } from "src/hooks/use-editor-markings"; +import { SummarySideBar } from "src/ui/components/summary-side-bar"; +import { DocumentDetails } from "src/types/editor-types"; +import { PageRenderer } from "src/ui/components/page-renderer"; +import { getMenuOptions } from "src/utils/menu-options"; import { useRouter } from "next/router"; -import { IEmbedConfig } from "./extensions/widgets/IssueEmbedWidget/types"; -import { UploadImage, DeleteImage, RestoreImage } from "@plane/editor-types"; +import { IEmbedConfig } from "src/ui/extensions/widgets/issue-embed-widget/types"; interface IDocumentEditor { // document info @@ -59,13 +58,6 @@ interface EditorHandle { setEditorValue: (content: string) => void; } -export interface IMarking { - type: "heading"; - level: number; - text: string; - sequence: number; -} - const DocumentEditor = ({ documentDetails, onChange, diff --git a/packages/editor/document-editor/src/ui/menu/fixed-menu.tsx b/packages/editor/document-editor/src/ui/menu/fixed-menu.tsx index f4b205484..cac42e1f9 100644 --- a/packages/editor/document-editor/src/ui/menu/fixed-menu.tsx +++ b/packages/editor/document-editor/src/ui/menu/fixed-menu.tsx @@ -17,8 +17,8 @@ import { HeadingThreeItem, findTableAncestor, EditorMenuItem, + UploadImage, } from "@plane/editor-core"; -import { UploadImage } from "@plane/editor-types"; export type BubbleMenuItem = EditorMenuItem; diff --git a/packages/editor/document-editor/src/ui/readonly/index.tsx b/packages/editor/document-editor/src/ui/readonly/index.tsx index e7897755e..7445bfd6d 100644 --- a/packages/editor/document-editor/src/ui/readonly/index.tsx +++ b/packages/editor/document-editor/src/ui/readonly/index.tsx @@ -1,15 +1,15 @@ import { getEditorClassNames, useReadOnlyEditor } from "@plane/editor-core"; import { useRouter } from "next/router"; import { useState, forwardRef, useEffect } from "react"; -import { EditorHeader } from "../components/editor-header"; -import { PageRenderer } from "../components/page-renderer"; -import { SummarySideBar } from "../components/summary-side-bar"; -import { IssueWidgetExtension } from "../extensions/widgets/IssueEmbedWidget"; -import { IEmbedConfig } from "../extensions/widgets/IssueEmbedWidget/types"; -import { useEditorMarkings } from "../hooks/use-editor-markings"; -import { DocumentDetails } from "../types/editor-types"; -import { IPageArchiveConfig, IPageLockConfig, IDuplicationConfig } from "../types/menu-actions"; -import { getMenuOptions } from "../utils/menu-options"; +import { EditorHeader } from "src/ui/components/editor-header"; +import { PageRenderer } from "src/ui/components/page-renderer"; +import { SummarySideBar } from "src/ui/components/summary-side-bar"; +import { IssueWidgetExtension } from "src/ui/extensions/widgets/issue-embed-widget"; +import { IEmbedConfig } from "src/ui/extensions/widgets/issue-embed-widget/types"; +import { useEditorMarkings } from "src/hooks/use-editor-markings"; +import { DocumentDetails } from "src/types/editor-types"; +import { IPageArchiveConfig, IPageLockConfig, IDuplicationConfig } from "src/types/menu-actions"; +import { getMenuOptions } from "src/utils/menu-options"; interface IDocumentReadOnlyEditor { value: string; diff --git a/packages/editor/document-editor/src/ui/utils/editor-summary-utils.ts b/packages/editor/document-editor/src/utils/editor-summary-utils.ts similarity index 94% rename from packages/editor/document-editor/src/ui/utils/editor-summary-utils.ts rename to packages/editor/document-editor/src/utils/editor-summary-utils.ts index 248f439e3..b5160fddd 100644 --- a/packages/editor/document-editor/src/ui/utils/editor-summary-utils.ts +++ b/packages/editor/document-editor/src/utils/editor-summary-utils.ts @@ -1,5 +1,5 @@ import { Editor } from "@tiptap/react"; -import { IMarking } from ".."; +import { IMarking } from "src/types/editor-types"; function findNthH1(editor: Editor, n: number, level: number): number { let count = 0; diff --git a/packages/editor/document-editor/src/ui/utils/menu-actions.ts b/packages/editor/document-editor/src/utils/menu-actions.ts similarity index 100% rename from packages/editor/document-editor/src/ui/utils/menu-actions.ts rename to packages/editor/document-editor/src/utils/menu-actions.ts diff --git a/packages/editor/document-editor/src/ui/utils/menu-options.ts b/packages/editor/document-editor/src/utils/menu-options.ts similarity index 93% rename from packages/editor/document-editor/src/ui/utils/menu-options.ts rename to packages/editor/document-editor/src/utils/menu-options.ts index 0b4d02476..befed424d 100644 --- a/packages/editor/document-editor/src/ui/utils/menu-options.ts +++ b/packages/editor/document-editor/src/utils/menu-options.ts @@ -1,19 +1,9 @@ import { Editor } from "@tiptap/react"; -import { - Archive, - ArchiveIcon, - ArchiveRestoreIcon, - ClipboardIcon, - Copy, - Link, - Lock, - Unlock, - XCircle, -} from "lucide-react"; +import { Archive, ArchiveRestoreIcon, ClipboardIcon, Copy, Link, Lock, Unlock } from "lucide-react"; import { NextRouter } from "next/router"; -import { IVerticalDropdownItemProps } from "../components/vertical-dropdown-menu"; -import { IDuplicationConfig, IPageArchiveConfig, IPageLockConfig } from "../types/menu-actions"; -import { copyMarkdownToClipboard, CopyPageLink } from "./menu-actions"; +import { IVerticalDropdownItemProps } from "src/ui/components/vertical-dropdown-menu"; +import { IDuplicationConfig, IPageArchiveConfig, IPageLockConfig } from "src/types/menu-actions"; +import { copyMarkdownToClipboard, CopyPageLink } from "src/utils/menu-actions"; export interface MenuOptionsProps { editor: Editor; diff --git a/packages/editor/document-editor/tsconfig.json b/packages/editor/document-editor/tsconfig.json index 57d0e9a74..c15534037 100644 --- a/packages/editor/document-editor/tsconfig.json +++ b/packages/editor/document-editor/tsconfig.json @@ -1,5 +1,15 @@ { "extends": "tsconfig/react-library.json", - "include": ["src/**/*", "index.d.ts"], - "exclude": ["dist", "build", "node_modules"] + "include": [ + "src/**/*", + "index.d.ts" + ], + "exclude": [ + "dist", + "build", + "node_modules" + ], + "compilerOptions": { + "baseUrl": "." + } } diff --git a/packages/editor/extensions/package.json b/packages/editor/extensions/package.json index 48abd7701..94801928c 100644 --- a/packages/editor/extensions/package.json +++ b/packages/editor/extensions/package.json @@ -29,12 +29,10 @@ }, "dependencies": { "@plane/editor-core": "*", - "@plane/editor-types": "*", - "@tiptap/core": "^2.1.7", - "@tiptap/pm": "^2.1.7", - "@tiptap/react": "^2.1.7", - "@tiptap/suggestion": "^2.0.4", - "eslint": "8.36.0", + "@tiptap/core": "^2.1.13", + "@tiptap/pm": "^2.1.13", + "@tiptap/react": "^2.1.13", + "@tiptap/suggestion": "^2.1.13", "eslint-config-next": "13.2.4", "lucide-react": "^0.294.0", "tippy.js": "^6.3.7" @@ -43,7 +41,7 @@ "@types/node": "18.15.3", "@types/react": "^18.2.42", "@types/react-dom": "^18.2.17", - "eslint": "^7.32.0", + "eslint": "8.36.0", "postcss": "^8.4.29", "tailwind-config-custom": "*", "tsconfig": "*", diff --git a/packages/editor/extensions/src/extensions/slash-commands.tsx b/packages/editor/extensions/src/extensions/slash-commands.tsx index 92152b43a..88e257cef 100644 --- a/packages/editor/extensions/src/extensions/slash-commands.tsx +++ b/packages/editor/extensions/src/extensions/slash-commands.tsx @@ -1,9 +1,8 @@ import { useState, useEffect, useCallback, ReactNode, useRef, useLayoutEffect } from "react"; import { Editor, Range, Extension } from "@tiptap/core"; -import Suggestion from "@tiptap/suggestion"; +import Suggestion, { SuggestionOptions } from "@tiptap/suggestion"; import { ReactRenderer } from "@tiptap/react"; import tippy from "tippy.js"; -import type { UploadImage, ISlashCommandItem, CommandProps } from "@plane/editor-types"; import { CaseSensitive, Code2, @@ -19,6 +18,9 @@ import { Table, } from "lucide-react"; import { + UploadImage, + ISlashCommandItem, + CommandProps, cn, insertTableCommand, toggleBlockquote, @@ -38,7 +40,11 @@ interface CommandItemProps { icon: ReactNode; } -const Command = Extension.create({ +export type SlashCommandOptions = { + suggestion: Omit; +}; + +const Command = Extension.create({ name: "slash-command", addOptions() { return { @@ -47,6 +53,10 @@ const Command = Extension.create({ command: ({ editor, range, props }: { editor: Editor; range: Range; props: any }) => { props.command({ editor, range }); }, + allow({ editor }: { editor: Editor }) { + return !editor.isActive("table"); + }, + allowSpaces: true, }, }; }, @@ -54,9 +64,6 @@ const Command = Extension.create({ return [ Suggestion({ editor: this.editor, - allow({ editor }) { - return !editor.isActive("table"); - }, ...this.options.suggestion, }), ]; @@ -173,7 +180,7 @@ const getSuggestionItems = key: "image", title: "Image", description: "Upload an image from your computer.", - searchTerms: ["photo", "picture", "media"], + searchTerms: ["img", "photo", "picture", "media"], icon: , command: ({ editor, range }: CommandProps) => { insertImageCommand(editor, uploadFile, setIsSubmitting, range); @@ -310,7 +317,7 @@ const renderItems = () => { let popup: any | null = null; return { - onStart: (props: { editor: Editor; clientRect: DOMRect }) => { + onStart: (props: { editor: Editor; clientRect?: (() => DOMRect | null) | null }) => { component = new ReactRenderer(CommandList, { props, // @ts-ignore @@ -328,7 +335,7 @@ const renderItems = () => { placement: "bottom-start", }); }, - onUpdate: (props: { editor: Editor; clientRect: DOMRect }) => { + onUpdate: (props: { editor: Editor; clientRect?: (() => DOMRect | null) | null }) => { component?.updateProps(props); popup && diff --git a/packages/editor/extensions/src/index.ts b/packages/editor/extensions/src/index.ts index 76461c2e6..c0532c594 100644 --- a/packages/editor/extensions/src/index.ts +++ b/packages/editor/extensions/src/index.ts @@ -1,2 +1,4 @@ -export { SlashCommand } from "./extensions/slash-commands"; -export { DragAndDrop } from "./extensions/drag-drop"; +import "src/styles/drag-drop.css"; + +export { SlashCommand } from "src/extensions/slash-commands"; +export { DragAndDrop } from "src/extensions/drag-drop"; diff --git a/packages/editor/extensions/src/styles/drag-drop.css b/packages/editor/extensions/src/styles/drag-drop.css new file mode 100644 index 000000000..d95a8654b --- /dev/null +++ b/packages/editor/extensions/src/styles/drag-drop.css @@ -0,0 +1,53 @@ +.drag-handle { + position: fixed; + opacity: 1; + transition: opacity ease-in 0.2s; + height: 18px; + width: 15px; + display: grid; + place-items: center; + z-index: 10; + cursor: grab; + border-radius: 2px; + background-color: rgb(var(--color-background-90)); +} + +.drag-handle:hover { + background-color: rgb(var(--color-background-80)); + transition: background-color 0.2s; +} + +.drag-handle.hidden { + opacity: 0; + pointer-events: none; +} + +@media screen and (max-width: 600px) { + .drag-handle { + display: none; + pointer-events: none; + } +} + +.drag-handle-container { + height: 15px; + width: 15px; + cursor: grab; + display: grid; + place-items: center; +} + +.drag-handle-dots { + height: 100%; + width: 12px; + display: grid; + grid-template-columns: repeat(2, 1fr); + place-items: center; +} + +.drag-handle-dot { + height: 2.75px; + width: 3px; + background-color: rgba(var(--color-text-200)); + border-radius: 50%; +} diff --git a/packages/editor/extensions/tsconfig.json b/packages/editor/extensions/tsconfig.json index 57d0e9a74..c15534037 100644 --- a/packages/editor/extensions/tsconfig.json +++ b/packages/editor/extensions/tsconfig.json @@ -1,5 +1,15 @@ { "extends": "tsconfig/react-library.json", - "include": ["src/**/*", "index.d.ts"], - "exclude": ["dist", "build", "node_modules"] + "include": [ + "src/**/*", + "index.d.ts" + ], + "exclude": [ + "dist", + "build", + "node_modules" + ], + "compilerOptions": { + "baseUrl": "." + } } diff --git a/packages/editor/lite-text-editor/package.json b/packages/editor/lite-text-editor/package.json index bcaa36a02..7882d60ff 100644 --- a/packages/editor/lite-text-editor/package.json +++ b/packages/editor/lite-text-editor/package.json @@ -30,8 +30,7 @@ }, "dependencies": { "@plane/editor-core": "*", - "@plane/ui": "*", - "@plane/editor-types": "*" + "@plane/ui": "*" }, "devDependencies": { "@types/node": "18.15.3", diff --git a/packages/editor/lite-text-editor/src/index.ts b/packages/editor/lite-text-editor/src/index.ts index f09ce54a4..c37d45039 100644 --- a/packages/editor/lite-text-editor/src/index.ts +++ b/packages/editor/lite-text-editor/src/index.ts @@ -1,3 +1,3 @@ -export { LiteTextEditor, LiteTextEditorWithRef } from "./ui"; -export { LiteReadOnlyEditor, LiteReadOnlyEditorWithRef } from "./ui/read-only"; -export type { IMentionSuggestion, IMentionHighlight } from "@plane/editor-types"; +export { LiteTextEditor, LiteTextEditorWithRef } from "src/ui"; +export { LiteReadOnlyEditor, LiteReadOnlyEditorWithRef } from "src/ui/read-only"; +export type { IMentionSuggestion, IMentionHighlight } from "@plane/editor-core"; diff --git a/packages/editor/lite-text-editor/src/ui/extensions/index.tsx b/packages/editor/lite-text-editor/src/ui/extensions/index.tsx index 4531e9516..527fd5674 100644 --- a/packages/editor/lite-text-editor/src/ui/extensions/index.tsx +++ b/packages/editor/lite-text-editor/src/ui/extensions/index.tsx @@ -1,4 +1,4 @@ -import { EnterKeyExtension } from "./enter-key-extension"; +import { EnterKeyExtension } from "src/ui/extensions/enter-key-extension"; export const LiteTextEditorExtensions = (onEnterKeyPress?: () => void) => [ // EnterKeyExtension(onEnterKeyPress), diff --git a/packages/editor/lite-text-editor/src/ui/index.tsx b/packages/editor/lite-text-editor/src/ui/index.tsx index 0eb0e20df..57774ab5d 100644 --- a/packages/editor/lite-text-editor/src/ui/index.tsx +++ b/packages/editor/lite-text-editor/src/ui/index.tsx @@ -1,8 +1,16 @@ import * as React from "react"; -import { EditorContainer, EditorContentWrapper, getEditorClassNames, useEditor } from "@plane/editor-core"; -import { FixedMenu } from "./menus/fixed-menu"; -import { LiteTextEditorExtensions } from "./extensions"; -import { UploadImage, DeleteImage, IMentionSuggestion, RestoreImage } from "@plane/editor-types"; +import { + UploadImage, + DeleteImage, + IMentionSuggestion, + RestoreImage, + EditorContainer, + EditorContentWrapper, + getEditorClassNames, + useEditor, +} from "@plane/editor-core"; +import { FixedMenu } from "src/ui/menus/fixed-menu"; +import { LiteTextEditorExtensions } from "src/ui/extensions"; interface ILiteTextEditor { value: string; diff --git a/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/index.tsx b/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/index.tsx index 95bd8d6dd..71ad4e0e1 100644 --- a/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/index.tsx +++ b/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/index.tsx @@ -9,27 +9,21 @@ import { ImageItem, isCellSelection, ItalicItem, + LucideIconType, NumberedListItem, QuoteItem, StrikeThroughItem, TableItem, UnderLineItem, + UploadImage, } from "@plane/editor-core"; import { Tooltip } from "@plane/ui"; -import type { SVGProps } from "react"; -import { UploadImage } from "@plane/editor-types"; -interface LucideProps extends Partial> { - size?: string | number; - absoluteStrokeWidth?: boolean; -} - -type LucideIcon = (props: LucideProps) => JSX.Element; export interface BubbleMenuItem { name: string; isActive: () => boolean; command: () => void; - icon: LucideIcon; + icon: LucideIconType; } type EditorBubbleMenuProps = { @@ -127,8 +121,8 @@ export const FixedMenu = (props: EditorBubbleMenuProps) => {
- {basicTextFormattingItems.map((item, index) => ( - {item.name}}> + {basicTextFormattingItems.map((item) => ( + {item.name}}>
- {listFormattingItems.map((item, index) => ( - {item.name}}> + {listFormattingItems.map((item) => ( + {item.name}}>
- {userActionItems.map((item, index) => ( - {item.name}}> + {userActionItems.map((item) => ( + {item.name}}>
- {complexItems.map((item, index) => ( - {item.name}}> + {complexItems.map((item) => ( + {item.name}}>
- } - position={position} - renderTarget={({ isOpen: isTooltipOpen, ref: eleReference, ...tooltipProps }) => - React.cloneElement(children, { - ref: eleReference, - ...tooltipProps, - ...children.props, - }) - } - /> - ); -}; diff --git a/packages/editor/lite-text-editor/tsconfig.json b/packages/editor/lite-text-editor/tsconfig.json index 57d0e9a74..c15534037 100644 --- a/packages/editor/lite-text-editor/tsconfig.json +++ b/packages/editor/lite-text-editor/tsconfig.json @@ -1,5 +1,15 @@ { "extends": "tsconfig/react-library.json", - "include": ["src/**/*", "index.d.ts"], - "exclude": ["dist", "build", "node_modules"] + "include": [ + "src/**/*", + "index.d.ts" + ], + "exclude": [ + "dist", + "build", + "node_modules" + ], + "compilerOptions": { + "baseUrl": "." + } } diff --git a/packages/editor/rich-text-editor/package.json b/packages/editor/rich-text-editor/package.json index baac553b8..245248d45 100644 --- a/packages/editor/rich-text-editor/package.json +++ b/packages/editor/rich-text-editor/package.json @@ -31,9 +31,8 @@ "dependencies": { "@plane/editor-core": "*", "@plane/editor-extensions": "*", - "@plane/editor-types": "*", - "@tiptap/core": "^2.1.11", - "@tiptap/extension-placeholder": "^2.1.11", + "@tiptap/core": "^2.1.13", + "@tiptap/extension-placeholder": "^2.1.13", "lucide-react": "^0.294.0" }, "devDependencies": { diff --git a/packages/editor/rich-text-editor/src/index.ts b/packages/editor/rich-text-editor/src/index.ts index 7dc0783d9..eb745c45b 100644 --- a/packages/editor/rich-text-editor/src/index.ts +++ b/packages/editor/rich-text-editor/src/index.ts @@ -1,4 +1,4 @@ -export { RichTextEditor, RichTextEditorWithRef } from "./ui"; -export { RichReadOnlyEditor, RichReadOnlyEditorWithRef } from "./ui/read-only"; -export type { RichTextEditorProps, IRichTextEditor } from "./ui"; -export type { IMentionHighlight, IMentionSuggestion } from "@plane/editor-types"; +export { RichTextEditor, RichTextEditorWithRef } from "src/ui"; +export { RichReadOnlyEditor, RichReadOnlyEditorWithRef } from "src/ui/read-only"; +export type { RichTextEditorProps, IRichTextEditor } from "src/ui"; +export type { IMentionHighlight, IMentionSuggestion } from "@plane/editor-core"; diff --git a/packages/editor/rich-text-editor/src/ui/extensions/index.tsx b/packages/editor/rich-text-editor/src/ui/extensions/index.tsx index 9a9d406b7..1e81c8173 100644 --- a/packages/editor/rich-text-editor/src/ui/extensions/index.tsx +++ b/packages/editor/rich-text-editor/src/ui/extensions/index.tsx @@ -1,7 +1,6 @@ -import { SlashCommand } from "@plane/editor-extensions"; +import { SlashCommand, DragAndDrop } from "@plane/editor-extensions"; import Placeholder from "@tiptap/extension-placeholder"; -import { DragAndDrop } from "@plane/editor-extensions"; -import { UploadImage } from "@plane/editor-types"; +import { UploadImage } from "@plane/editor-core"; export const RichTextEditorExtensions = ( uploadFile: UploadImage, diff --git a/packages/editor/rich-text-editor/src/ui/index.tsx b/packages/editor/rich-text-editor/src/ui/index.tsx index 5d34eb85d..17d701600 100644 --- a/packages/editor/rich-text-editor/src/ui/index.tsx +++ b/packages/editor/rich-text-editor/src/ui/index.tsx @@ -1,9 +1,17 @@ "use client"; import * as React from "react"; -import { EditorContainer, EditorContentWrapper, getEditorClassNames, useEditor } from "@plane/editor-core"; -import { EditorBubbleMenu } from "./menus/bubble-menu"; -import { RichTextEditorExtensions } from "./extensions"; -import { DeleteImage, IMentionSuggestion, RestoreImage, UploadImage } from "@plane/editor-types"; +import { + DeleteImage, + EditorContainer, + EditorContentWrapper, + getEditorClassNames, + IMentionSuggestion, + RestoreImage, + UploadImage, + useEditor, +} from "@plane/editor-core"; +import { EditorBubbleMenu } from "src/ui/menus/bubble-menu"; +import { RichTextEditorExtensions } from "src/ui/extensions"; export type IRichTextEditor = { value: string; diff --git a/packages/editor/rich-text-editor/src/ui/menus/bubble-menu/index.tsx b/packages/editor/rich-text-editor/src/ui/menus/bubble-menu/index.tsx index 7e9c834a7..2e7dd25b8 100644 --- a/packages/editor/rich-text-editor/src/ui/menus/bubble-menu/index.tsx +++ b/packages/editor/rich-text-editor/src/ui/menus/bubble-menu/index.tsx @@ -1,15 +1,15 @@ import { BubbleMenu, BubbleMenuProps, isNodeSelection } from "@tiptap/react"; import { FC, useEffect, useState } from "react"; -import { BoldIcon } from "lucide-react"; -import { NodeSelector } from "./node-selector"; -import { LinkSelector } from "./link-selector"; +import { NodeSelector } from "src/ui/menus/bubble-menu/node-selector"; +import { LinkSelector } from "src/ui/menus/bubble-menu/link-selector"; import { BoldItem, cn, CodeItem, isCellSelection, ItalicItem, + LucideIconType, StrikeThroughItem, UnderLineItem, } from "@plane/editor-core"; @@ -18,7 +18,7 @@ export interface BubbleMenuItem { name: string; isActive: () => boolean; command: () => void; - icon: typeof BoldIcon; + icon: LucideIconType; } type EditorBubbleMenuProps = Omit; @@ -117,9 +117,9 @@ export const EditorBubbleMenu: FC = (props: any) => { }} />
- {items.map((item, index) => ( + {items.map((item) => ( - )} - {comicBox && - isHovered && - (comicBox.direction === "right" ? ( -
+ {primaryButton && ( + + )} + {comicBox && + isHovered && + (comicBox.direction === "right" ? ( +
+
+
+
+
+

+

{comicBox?.title}

+

{comicBox?.description}

+ +
-
-

-

{comicBox?.title}

-

{comicBox?.description}

- + ) : ( +
+
+
+
+
+

+

{comicBox?.title}

+

{comicBox?.description}

+ +
-
- ) : ( -
-
-
-
-
-

-

{comicBox?.title}

-

{comicBox?.description}

- -
-
- ))} + ))} +
diff --git a/web/components/core/activity.tsx b/web/components/core/activity.tsx index 37f8d2626..1ac34cf73 100644 --- a/web/components/core/activity.tsx +++ b/web/components/core/activity.tsx @@ -11,7 +11,6 @@ import { CopyPlus, Calendar, Link2Icon, - RocketIcon, Users2Icon, ArchiveIcon, PaperclipIcon, @@ -48,8 +47,8 @@ const IssueLink = ({ activity }: { activity: IIssueActivity }) => { rel={activity.issue === null ? "" : "noopener noreferrer"} className="inline-flex items-center gap-1 font-medium text-custom-text-100 hover:underline" > - {activity.issue_detail ? `${activity.project_detail.identifier}-${activity.issue_detail.sequence_id}` : "Issue"} - + {activity.issue_detail ? `${activity.project_detail.identifier}-${activity.issue_detail.sequence_id}` : "Issue"}{" "} + {activity.issue_detail?.name} ); @@ -163,7 +162,6 @@ const activityDetails: { className="inline-flex items-center gap-1 font-medium text-custom-text-100 hover:underline" > attachment - {showIssue && ( <> @@ -239,7 +237,6 @@ const activityDetails: { className="inline-flex items-center gap-1 truncate font-medium text-custom-text-100 hover:underline" > {activity.new_value} - ); @@ -254,7 +251,6 @@ const activityDetails: { className="inline-flex items-center gap-1 truncate font-medium text-custom-text-100 hover:underline" > {activity.new_value} - ); @@ -269,7 +265,6 @@ const activityDetails: { className="inline-flex items-center gap-1 truncate font-medium text-custom-text-100 hover:underline" > {activity.old_value} - ); @@ -398,7 +393,6 @@ const activityDetails: { className="inline-flex items-center gap-1 font-medium text-custom-text-100 hover:underline" > link - {showIssue && ( <> @@ -420,7 +414,6 @@ const activityDetails: { className="inline-flex items-center gap-1 font-medium text-custom-text-100 hover:underline" > link - {showIssue && ( <> @@ -442,7 +435,6 @@ const activityDetails: { className="inline-flex items-center gap-1 font-medium text-custom-text-100 hover:underline" > link - {showIssue && ( <> @@ -469,7 +461,6 @@ const activityDetails: { className="inline-flex items-center gap-1 truncate font-medium text-custom-text-100 hover:underline" > {activity.new_value} - ); @@ -484,7 +475,6 @@ const activityDetails: { className="inline-flex items-center gap-1 truncate font-medium text-custom-text-100 hover:underline" > {activity.new_value} - ); @@ -499,7 +489,6 @@ const activityDetails: { className="inline-flex items-center gap-1 truncate font-medium text-custom-text-100 hover:underline" > {activity.old_value} - ); diff --git a/web/components/cycles/active-cycle-details.tsx b/web/components/cycles/active-cycle-details.tsx index ea982099f..47beaa262 100644 --- a/web/components/cycles/active-cycle-details.tsx +++ b/web/components/cycles/active-cycle-details.tsx @@ -28,7 +28,7 @@ import { ViewIssueLabel } from "components/issues"; // icons import { AlarmClock, AlertTriangle, ArrowRight, CalendarDays, Star, Target } from "lucide-react"; // helpers -import { getDateRangeStatus, renderShortDateWithYearFormat, findHowManyDaysLeft } from "helpers/date-time.helper"; +import { renderShortDateWithYearFormat, findHowManyDaysLeft } from "helpers/date-time.helper"; import { truncateText } from "helpers/string.helper"; // types import { ICycle } from "types"; @@ -137,7 +137,7 @@ export const ActiveCycleDetails: React.FC = observer((props cancelled: cycle.cancelled_issues, }; - const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date); + const cycleStatus = cycle.status.toLocaleLowerCase(); const handleAddToFavorites = (e: MouseEvent) => { e.preventDefault(); diff --git a/web/components/cycles/cycles-board-card.tsx b/web/components/cycles/cycles-board-card.tsx index f020b0998..d43d56872 100644 --- a/web/components/cycles/cycles-board-card.tsx +++ b/web/components/cycles/cycles-board-card.tsx @@ -10,15 +10,10 @@ import { Avatar, AvatarGroup, CustomMenu, Tooltip, LayersIcon, CycleGroupIcon } // icons import { Info, LinkIcon, Pencil, Star, Trash2 } from "lucide-react"; // helpers -import { - getDateRangeStatus, - findHowManyDaysLeft, - renderShortDate, - renderShortMonthDate, -} from "helpers/date-time.helper"; +import { findHowManyDaysLeft, renderShortDate, renderShortMonthDate } from "helpers/date-time.helper"; import { copyTextToClipboard } from "helpers/string.helper"; // types -import { ICycle } from "types"; +import { ICycle, TCycleGroups } from "types"; // store import { useMobxStore } from "lib/mobx/store-provider"; // constants @@ -45,7 +40,7 @@ export const CyclesBoardCard: FC = (props) => { const [updateModal, setUpdateModal] = useState(false); const [deleteModal, setDeleteModal] = useState(false); // computed - const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date); + const cycleStatus = cycle.status.toLocaleLowerCase() as TCycleGroups; const isCompleted = cycleStatus === "completed"; const endDate = new Date(cycle.end_date ?? ""); const startDate = new Date(cycle.start_date ?? ""); diff --git a/web/components/cycles/cycles-list-item.tsx b/web/components/cycles/cycles-list-item.tsx index 86b3bffa9..9ea26ab39 100644 --- a/web/components/cycles/cycles-list-item.tsx +++ b/web/components/cycles/cycles-list-item.tsx @@ -13,15 +13,10 @@ import { CustomMenu, Tooltip, CircularProgressIndicator, CycleGroupIcon, AvatarG // icons import { Check, Info, LinkIcon, Pencil, Star, Trash2, User2 } from "lucide-react"; // helpers -import { - getDateRangeStatus, - findHowManyDaysLeft, - renderShortDate, - renderShortMonthDate, -} from "helpers/date-time.helper"; +import { findHowManyDaysLeft, renderShortDate, renderShortMonthDate } from "helpers/date-time.helper"; import { copyTextToClipboard } from "helpers/string.helper"; // types -import { ICycle } from "types"; +import { ICycle, TCycleGroups } from "types"; // constants import { CYCLE_STATUS } from "constants/cycle"; import { EUserWorkspaceRoles } from "constants/workspace"; @@ -50,7 +45,7 @@ export const CyclesListItem: FC = (props) => { const [updateModal, setUpdateModal] = useState(false); const [deleteModal, setDeleteModal] = useState(false); // computed - const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date); + const cycleStatus = cycle.status.toLocaleLowerCase() as TCycleGroups; const isCompleted = cycleStatus === "completed"; const endDate = new Date(cycle.end_date ?? ""); const startDate = new Date(cycle.start_date ?? ""); diff --git a/web/components/cycles/gantt-chart/blocks.tsx b/web/components/cycles/gantt-chart/blocks.tsx index 03614592c..76a4d9235 100644 --- a/web/components/cycles/gantt-chart/blocks.tsx +++ b/web/components/cycles/gantt-chart/blocks.tsx @@ -3,7 +3,7 @@ import { useRouter } from "next/router"; // ui import { Tooltip, ContrastIcon } from "@plane/ui"; // helpers -import { getDateRangeStatus, renderShortDate } from "helpers/date-time.helper"; +import { renderShortDate } from "helpers/date-time.helper"; // types import { ICycle } from "types"; @@ -11,8 +11,7 @@ export const CycleGanttBlock = ({ data }: { data: ICycle }) => { const router = useRouter(); const { workspaceSlug } = router.query; - const cycleStatus = getDateRangeStatus(data?.start_date, data?.end_date); - + const cycleStatus = data.status.toLocaleLowerCase(); return (
{ cycleStatus === "current" ? "#09a953" : cycleStatus === "upcoming" - ? "#f7ae59" - : cycleStatus === "completed" - ? "#3f76ff" - : cycleStatus === "draft" - ? "rgb(var(--color-text-200))" - : "", + ? "#f7ae59" + : cycleStatus === "completed" + ? "#3f76ff" + : cycleStatus === "draft" + ? "rgb(var(--color-text-200))" + : "", }} onClick={() => router.push(`/${workspaceSlug}/projects/${data?.project}/cycles/${data?.id}`)} > @@ -52,7 +51,7 @@ export const CycleGanttSidebarBlock = ({ data }: { data: ICycle }) => { const router = useRouter(); const { workspaceSlug } = router.query; - const cycleStatus = getDateRangeStatus(data?.start_date, data?.end_date); + const cycleStatus = data.status.toLocaleLowerCase(); return (
{ cycleStatus === "current" ? "#09a953" : cycleStatus === "upcoming" - ? "#f7ae59" - : cycleStatus === "completed" - ? "#3f76ff" - : cycleStatus === "draft" - ? "rgb(var(--color-text-200))" - : "" + ? "#f7ae59" + : cycleStatus === "completed" + ? "#3f76ff" + : cycleStatus === "draft" + ? "rgb(var(--color-text-200))" + : "" }`} />
{data?.name}
diff --git a/web/components/cycles/sidebar.tsx b/web/components/cycles/sidebar.tsx index 1fd1cd05c..18c233d6c 100644 --- a/web/components/cycles/sidebar.tsx +++ b/web/components/cycles/sidebar.tsx @@ -17,12 +17,20 @@ import { CycleDeleteModal } from "components/cycles/delete-modal"; import { CustomRangeDatePicker } from "components/ui"; import { Avatar, CustomMenu, Loader, LayersIcon } from "@plane/ui"; // icons -import { ChevronDown, LinkIcon, Trash2, UserCircle2, AlertCircle, ChevronRight, MoveRight } from "lucide-react"; +import { + ChevronDown, + LinkIcon, + Trash2, + UserCircle2, + AlertCircle, + ChevronRight, + CalendarCheck2, + CalendarClock, +} from "lucide-react"; // helpers import { copyUrlToClipboard } from "helpers/string.helper"; import { findHowManyDaysLeft, - getDateRangeStatus, isDateGreaterThanToday, renderDateFormat, renderShortDate, @@ -266,10 +274,7 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { [workspaceSlug, projectId, cycleId, issueFilters, updateFilters] ); - const cycleStatus = - cycleDetails?.start_date && cycleDetails?.end_date - ? getDateRangeStatus(cycleDetails?.start_date, cycleDetails?.end_date) - : "draft"; + const cycleStatus = cycleDetails.status.toLocaleLowerCase(); const isCompleted = cycleStatus === "completed"; const isStartValid = new Date(`${cycleDetails?.start_date}`) <= new Date(); @@ -357,8 +362,7 @@ export const CycleDetailsSidebar: React.FC = observer((props) => {
-
-

{cycleDetails.name}

+
{currentCycle && ( = observer((props) => { : `${currentCycle.label}`} )} -
- +
+

{cycleDetails.name}

+
+ + {cycleDetails.description && ( + + {cycleDetails.description} + + )} + +
+
+
+ + Start Date +
+
+ - {areYearsEqual ? renderShortDate(startDate, "_ _") : renderShortMonthDate(startDate, "_ _")} + + {areYearsEqual + ? renderShortDate(startDate, "No date selected") + : renderShortMonthDate(startDate, "No date selected")} + = observer((props) => { leaveFrom="opacity-100 translate-y-0" leaveTo="opacity-0 translate-y-1" > - + { @@ -410,16 +438,32 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { - - +
+
+ +
+
+ + Target Date +
+
+ <> - {areYearsEqual ? renderShortDate(endDate, "_ _") : renderShortMonthDate(endDate, "_ _")} + + {areYearsEqual + ? renderShortDate(endDate, "No date selected") + : renderShortMonthDate(endDate, "No date selected")} + = observer((props) => { leaveFrom="opacity-100 translate-y-0" leaveTo="opacity-0 translate-y-1" > - + { @@ -451,15 +495,7 @@ export const CycleDetailsSidebar: React.FC = observer((props) => {
-
- {cycleDetails.description && ( - - {cycleDetails.description} - - )} - -
diff --git a/web/components/cycles/transfer-issues-modal.tsx b/web/components/cycles/transfer-issues-modal.tsx index 55555e221..dd462e360 100644 --- a/web/components/cycles/transfer-issues-modal.tsx +++ b/web/components/cycles/transfer-issues-modal.tsx @@ -15,8 +15,6 @@ import { AlertCircle, Search, X } from "lucide-react"; import { INCOMPLETE_CYCLES_LIST } from "constants/fetch-keys"; // types import { ICycle } from "types"; -//helper -import { getDateRangeStatus } from "helpers/date-time.helper"; type Props = { isOpen: boolean; @@ -138,7 +136,7 @@ export const TransferIssuesModal: React.FC = observer(({ isOpen, handleCl
{option?.name} - {getDateRangeStatus(option?.start_date, option?.end_date)} + {option.status}
diff --git a/web/components/estimates/create-update-estimate-modal.tsx b/web/components/estimates/create-update-estimate-modal.tsx index 6f0f98e86..b24172688 100644 --- a/web/components/estimates/create-update-estimate-modal.tsx +++ b/web/components/estimates/create-update-estimate-modal.tsx @@ -129,6 +129,22 @@ export const CreateUpdateEstimateModal: React.FC = observer((props) => { return; } + if ( + formData.value1.length > 20 || + formData.value2.length > 20 || + formData.value3.length > 20 || + formData.value4.length > 20 || + formData.value5.length > 20 || + formData.value6.length > 20 + ) { + setToastAlert({ + type: "error", + title: "Error!", + message: "Estimate point cannot have more than 20 characters.", + }); + return; + } + if ( checkDuplicates([ formData.value1, @@ -269,6 +285,12 @@ export const CreateUpdateEstimateModal: React.FC = observer((props) => { ( = observer((props) => { ? "Updating Estimate..." : "Update Estimate" : isSubmitting - ? "Creating Estimate..." - : "Create Estimate"} + ? "Creating Estimate..." + : "Create Estimate"}
diff --git a/web/components/headers/global-issues.tsx b/web/components/headers/global-issues.tsx index ef4c2b3f5..0b27bdd81 100644 --- a/web/components/headers/global-issues.tsx +++ b/web/components/headers/global-issues.tsx @@ -95,7 +95,7 @@ export const GlobalIssuesHeader: React.FC = observer((props) => { ) } - label={`Workspace ${activeLayout === "spreadsheet" ? "Issues" : "Views"}`} + label={`All ${activeLayout === "spreadsheet" ? "Issues" : "Views"}`} />
diff --git a/web/components/instance/email-form.tsx b/web/components/instance/email-form.tsx index 82da7553e..9d23f398f 100644 --- a/web/components/instance/email-form.tsx +++ b/web/components/instance/email-form.tsx @@ -67,7 +67,7 @@ export const InstanceEmailForm: FC = (props) => { return ( <> -
+

Host

= (props) => { />
-
+

Username

= (props) => {
-
+

From address

= (props) => { )} />

- You will have to verify your email address to being sending emails. + This is the email address your users will see when getting emails from this instance. You will need to + verify this address.

-
+
diff --git a/web/components/instance/setup-form/sign-in-form.tsx b/web/components/instance/setup-form/sign-in-form.tsx index c4fd0dbe0..1f3bf61f7 100644 --- a/web/components/instance/setup-form/sign-in-form.tsx +++ b/web/components/instance/setup-form/sign-in-form.tsx @@ -90,7 +90,7 @@ export const InstanceSetupSignInForm: FC = (props) => { type="email" value={value} onChange={onChange} - placeholder="orville.wright@firstflight.com" + placeholder="orville.wright@frstflt.com" className="h-[46px] w-full border border-onboarding-border-100 pr-12 placeholder:text-onboarding-text-400" /> {value.length > 0 && ( diff --git a/web/components/issues/issue-layouts/filters/applied-filters/roots/project-root.tsx b/web/components/issues/issue-layouts/filters/applied-filters/roots/project-root.tsx index 6d7369bb9..31317366c 100644 --- a/web/components/issues/issue-layouts/filters/applied-filters/roots/project-root.tsx +++ b/web/components/issues/issue-layouts/filters/applied-filters/roots/project-root.tsx @@ -1,29 +1,32 @@ import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; - // mobx store import { useMobxStore } from "lib/mobx/store-provider"; // components import { AppliedFiltersList, SaveFilterView } from "components/issues"; - // types import { IIssueFilterOptions } from "types"; import { EFilterType } from "store/issues/types"; +// constants +import { EUserWorkspaceRoles } from "constants/workspace"; export const ProjectAppliedFiltersRoot: React.FC = observer(() => { + // router const router = useRouter(); const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string; }; - + // mobx stores const { projectLabel: { projectLabels }, projectState: projectStateStore, projectMember: { projectMembers }, projectIssuesFilter: { issueFilters, updateFilters }, + user: { currentProjectRole }, } = useMobxStore(); - + // derived values + const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER; const userFilters = issueFilters?.filters; // filters whose value not null or empty array @@ -73,8 +76,9 @@ export const ProjectAppliedFiltersRoot: React.FC = observer(() => { members={projectMembers?.map((m) => m.member)} states={projectStateStore.states?.[projectId?.toString() ?? ""]} /> - - + {isEditingAllowed && ( + + )}
); }); diff --git a/web/components/issues/issue-layouts/kanban/properties.tsx b/web/components/issues/issue-layouts/kanban/properties.tsx index 9590c9068..5be5a12c5 100644 --- a/web/components/issues/issue-layouts/kanban/properties.tsx +++ b/web/components/issues/issue-layouts/kanban/properties.tsx @@ -166,7 +166,7 @@ export const KanBanProperties: React.FC = observer((props) => {/* sub-issues */} {displayProperties && displayProperties?.sub_issue_count && !!issue?.sub_issues_count && ( -
+
{issue.sub_issues_count}
@@ -176,7 +176,7 @@ export const KanBanProperties: React.FC = observer((props) => {/* attachments */} {displayProperties && displayProperties?.attachment_count && !!issue?.attachment_count && ( -
+
{issue.attachment_count}
@@ -186,7 +186,7 @@ export const KanBanProperties: React.FC = observer((props) => {/* link */} {displayProperties && displayProperties?.link && !!issue?.link_count && ( -
+
{issue.link_count}
diff --git a/web/components/issues/issue-layouts/kanban/quick-add-issue-form.tsx b/web/components/issues/issue-layouts/kanban/quick-add-issue-form.tsx index 0120e02f6..9c4406f7d 100644 --- a/web/components/issues/issue-layouts/kanban/quick-add-issue-form.tsx +++ b/web/components/issues/issue-layouts/kanban/quick-add-issue-form.tsx @@ -22,7 +22,7 @@ const Inputs = (props: any) => { }, [setFocus]); return ( -
+

{projectDetail?.identifier ?? "..."}

= observer((props) => { {/* sub-issues */} {displayProperties && displayProperties?.sub_issue_count && !!issue?.sub_issues_count && ( -
+
{issue.sub_issues_count}
@@ -147,7 +147,7 @@ export const ListProperties: FC = observer((props) => { {/* attachments */} {displayProperties && displayProperties?.attachment_count && !!issue?.attachment_count && ( -
+
{issue.attachment_count}
@@ -157,7 +157,7 @@ export const ListProperties: FC = observer((props) => { {/* link */} {displayProperties && displayProperties?.link && !!issue?.link_count && ( -
+
{issue.link_count}
diff --git a/web/components/issues/issue-layouts/properties/date.tsx b/web/components/issues/issue-layouts/properties/date.tsx index cfe3481e3..d0bb29711 100644 --- a/web/components/issues/issue-layouts/properties/date.tsx +++ b/web/components/issues/issue-layouts/properties/date.tsx @@ -61,6 +61,7 @@ export const IssuePropertyDate: React.FC = observer((props) ref={dropdownBtn} className="border-none outline-none" onClick={(e) => e.stopPropagation()} + disabled={disabled} > = observer((props)
diff --git a/web/components/issues/issue-layouts/properties/labels.tsx b/web/components/issues/issue-layouts/properties/labels.tsx index 282268d7b..d0045c3d4 100644 --- a/web/components/issues/issue-layouts/properties/labels.tsx +++ b/web/components/issues/issue-layouts/properties/labels.tsx @@ -128,7 +128,11 @@ export const IssuePropertyLabels: React.FC = observer((pro ))} ) : ( -
+
= observer((pro ) : (
diff --git a/web/components/issues/issue-layouts/quick-action-dropdowns/archived-issue.tsx b/web/components/issues/issue-layouts/quick-action-dropdowns/archived-issue.tsx index 07631a7de..133cce1f9 100644 --- a/web/components/issues/issue-layouts/quick-action-dropdowns/archived-issue.tsx +++ b/web/components/issues/issue-layouts/quick-action-dropdowns/archived-issue.tsx @@ -23,7 +23,7 @@ export const ArchivedIssueQuickActions: React.FC = (props) => const { setToastAlert } = useToast(); const handleCopyIssueLink = () => { - copyUrlToClipboard(`/${workspaceSlug}/projects/${issue.project}/archived-issues/${issue.id}`).then(() => + copyUrlToClipboard(`${workspaceSlug}/projects/${issue.project}/archived-issues/${issue.id}`).then(() => setToastAlert({ type: "success", title: "Link copied", diff --git a/web/components/issues/issue-layouts/quick-action-dropdowns/cycle-issue.tsx b/web/components/issues/issue-layouts/quick-action-dropdowns/cycle-issue.tsx index 5faace440..af018a652 100644 --- a/web/components/issues/issue-layouts/quick-action-dropdowns/cycle-issue.tsx +++ b/web/components/issues/issue-layouts/quick-action-dropdowns/cycle-issue.tsx @@ -27,7 +27,7 @@ export const CycleIssueQuickActions: React.FC = (props) => { const { setToastAlert } = useToast(); const handleCopyIssueLink = () => { - copyUrlToClipboard(`/${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`).then(() => + copyUrlToClipboard(`${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`).then(() => setToastAlert({ type: "success", title: "Link copied", diff --git a/web/components/issues/issue-layouts/quick-action-dropdowns/module-issue.tsx b/web/components/issues/issue-layouts/quick-action-dropdowns/module-issue.tsx index 7bd35e321..0ad1f610b 100644 --- a/web/components/issues/issue-layouts/quick-action-dropdowns/module-issue.tsx +++ b/web/components/issues/issue-layouts/quick-action-dropdowns/module-issue.tsx @@ -27,7 +27,7 @@ export const ModuleIssueQuickActions: React.FC = (props) => { const { setToastAlert } = useToast(); const handleCopyIssueLink = () => { - copyUrlToClipboard(`/${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`).then(() => + copyUrlToClipboard(`${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`).then(() => setToastAlert({ type: "success", title: "Link copied", diff --git a/web/components/issues/issue-layouts/quick-action-dropdowns/project-issue.tsx b/web/components/issues/issue-layouts/quick-action-dropdowns/project-issue.tsx index 0f4fe28ce..12438b2a3 100644 --- a/web/components/issues/issue-layouts/quick-action-dropdowns/project-issue.tsx +++ b/web/components/issues/issue-layouts/quick-action-dropdowns/project-issue.tsx @@ -37,7 +37,7 @@ export const ProjectIssueQuickActions: React.FC = (props) => const { setToastAlert } = useToast(); const handleCopyIssueLink = () => { - copyUrlToClipboard(`/${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`).then(() => + copyUrlToClipboard(`${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`).then(() => setToastAlert({ type: "success", title: "Link copied", diff --git a/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx b/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx index f967956f0..f77dfbed4 100644 --- a/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx @@ -17,8 +17,6 @@ import { import { TransferIssues, TransferIssuesModal } from "components/cycles"; // ui import { Spinner } from "@plane/ui"; -// helpers -import { getDateRangeStatus } from "helpers/date-time.helper"; export const CycleLayoutRoot: React.FC = observer(() => { const [transferIssuesModal, setTransferIssuesModal] = useState(false); @@ -50,10 +48,7 @@ export const CycleLayoutRoot: React.FC = observer(() => { const activeLayout = issueFilters?.displayFilters?.layout; const cycleDetails = cycleId ? cycleStore.cycle_details[cycleId.toString()] : undefined; - const cycleStatus = - cycleDetails?.start_date && cycleDetails?.end_date - ? getDateRangeStatus(cycleDetails?.start_date, cycleDetails?.end_date) - : "draft"; + const cycleStatus = cycleDetails?.status.toLocaleLowerCase() ?? "draft"; return ( <> diff --git a/web/components/issues/modal.tsx b/web/components/issues/modal.tsx index 43ac5ad71..0e4dee36d 100644 --- a/web/components/issues/modal.tsx +++ b/web/components/issues/modal.tsx @@ -187,7 +187,7 @@ export const CreateUpdateIssueModal: React.FC = observer((prop */ const onDiscardClose = () => { - if (formDirtyState !== null) { + if (formDirtyState !== null && formDirtyState.name.trim() !== "") { setShowConfirmDiscard(true); } else { handleClose(); diff --git a/web/components/issues/peek-overview/activity/card.tsx b/web/components/issues/peek-overview/activity/card.tsx index b8acb63d9..86d1a138c 100644 --- a/web/components/issues/peek-overview/activity/card.tsx +++ b/web/components/issues/peek-overview/activity/card.tsx @@ -88,7 +88,7 @@ export const IssueActivityCard: FC = (props) => {
-
+
{activityItem.field === "archived_at" && activityItem.new_value !== "restore" ? ( Plane ) : activityItem.actor_detail.is_bot ? ( @@ -101,8 +101,8 @@ export const IssueActivityCard: FC = (props) => { : activityItem.actor_detail.display_name} - )}{" "} - {message}{" "} + )} + {message} = observer((props) => { return ( + + {issueDetails?.parent && ( + )} - {issueDetails?.parent && } - +
); }; diff --git a/web/components/issues/view-select/due-date.tsx b/web/components/issues/view-select/due-date.tsx index d7e00148e..5b2bfb0ec 100644 --- a/web/components/issues/view-select/due-date.tsx +++ b/web/components/issues/view-select/due-date.tsx @@ -61,9 +61,9 @@ export const ViewDueDateSelect: React.FC = ({ className={`bg-transparent ${issue?.target_date ? "w-[6.5rem]" : "w-[5rem] text-center"}`} customInput={
{issue.target_date ? ( <> diff --git a/web/components/issues/view-select/start-date.tsx b/web/components/issues/view-select/start-date.tsx index c1408f015..8cfac4a64 100644 --- a/web/components/issues/view-select/start-date.tsx +++ b/web/components/issues/view-select/start-date.tsx @@ -49,9 +49,9 @@ export const ViewStartDateSelect: React.FC = ({ handleOnOpen={handleOnOpen} customInput={
{issue?.start_date ? ( <> diff --git a/web/components/labels/project-setting-label-list.tsx b/web/components/labels/project-setting-label-list.tsx index 75fc40329..1dea90d01 100644 --- a/web/components/labels/project-setting-label-list.tsx +++ b/web/components/labels/project-setting-label-list.tsx @@ -217,7 +217,7 @@ export const ProjectSettingsLabelList: React.FC = observer(() => { {projectLabels && projectLabels.length === 0 && ( = ({ value, onChange }) => { value="" className="flex cursor-pointer select-none items-center justify-between gap-2 truncate rounded px-1 py-1.5 text-custom-text-200" > - - No Lead - +
+ + No lead +
} onChange={onChange} diff --git a/web/components/modules/sidebar-select/select-lead.tsx b/web/components/modules/sidebar-select/select-lead.tsx index f675925c4..91a70504f 100644 --- a/web/components/modules/sidebar-select/select-lead.tsx +++ b/web/components/modules/sidebar-select/select-lead.tsx @@ -31,6 +31,17 @@ export const SidebarLeadSelect: FC = (props) => { : null ); + const noLeadOption = { + value: "", + query: "No lead", + content: ( +
+ + No lead +
+ ), + }; + const options = members?.map((member) => ({ value: member.member.id, query: member.member.display_name, @@ -42,6 +53,8 @@ export const SidebarLeadSelect: FC = (props) => { ), })); + const leadOption = (options || []).concat(noLeadOption); + const selectedOption = members?.find((m) => m.member.id === value)?.member; return ( @@ -69,7 +82,7 @@ export const SidebarLeadSelect: FC = (props) => {
) } - options={options} + options={leadOption} maxHeight="md" onChange={onChange} /> diff --git a/web/components/modules/sidebar.tsx b/web/components/modules/sidebar.tsx index 3c9552465..3fe7bf57e 100644 --- a/web/components/modules/sidebar.tsx +++ b/web/components/modules/sidebar.tsx @@ -15,7 +15,17 @@ import ProgressChart from "components/core/sidebar/progress-chart"; import { CustomRangeDatePicker } from "components/ui"; import { CustomMenu, Loader, LayersIcon, CustomSelect, ModuleStatusIcon } from "@plane/ui"; // icon -import { AlertCircle, ChevronDown, ChevronRight, Info, LinkIcon, MoveRight, Plus, Trash2 } from "lucide-react"; +import { + AlertCircle, + CalendarCheck2, + CalendarClock, + ChevronDown, + ChevronRight, + Info, + LinkIcon, + Plus, + Trash2, +} from "lucide-react"; // helpers import { isDateGreaterThanToday, @@ -227,7 +237,13 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => { else newValues.push(value); } - updateFilters(workspaceSlug.toString(), projectId.toString(), EFilterType.FILTERS, { [key]: newValues }, moduleId); + updateFilters( + workspaceSlug.toString(), + projectId.toString(), + EFilterType.FILTERS, + { [key]: newValues }, + moduleId + ); }, [workspaceSlug, projectId, moduleId, issueFilters, updateFilters] ); @@ -328,8 +344,7 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => {
-

{moduleDetails.name}

-
+
= observer((props) => { )} /> +
+

{moduleDetails.name}

+
-
- + {moduleDetails.description && ( + + {moduleDetails.description} + + )} + +
+
+
+ + + Start Date +
+
+ - {areYearsEqual ? renderShortDate(startDate, "_ _") : renderShortMonthDate(startDate, "_ _")} + + {areYearsEqual + ? renderShortDate(startDate, "No date selected") + : renderShortMonthDate(startDate, "No date selected")} + = observer((props) => { leaveFrom="opacity-100 translate-y-0" leaveTo="opacity-0 translate-y-1" > - + { @@ -402,16 +441,32 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => { - - +
+
+ +
+
+ + Target Date +
+
+ <> - {areYearsEqual ? renderShortDate(endDate, "_ _") : renderShortMonthDate(endDate, "_ _")} + + {areYearsEqual + ? renderShortDate(endDate, "No date selected") + : renderShortMonthDate(endDate, "No date selected")} + = observer((props) => { leaveFrom="opacity-100 translate-y-0" leaveTo="opacity-0 translate-y-1" > - + { @@ -442,15 +497,6 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => {
-
- - {moduleDetails.description && ( - - {moduleDetails.description} - - )} - -
= (props) => { const { diff --git a/web/components/page-views/workspace-dashboard.tsx b/web/components/page-views/workspace-dashboard.tsx index ff378e23d..299c35de9 100644 --- a/web/components/page-views/workspace-dashboard.tsx +++ b/web/components/page-views/workspace-dashboard.tsx @@ -37,8 +37,7 @@ export const WorkspaceDashboardView = observer(() => { workspaceSlug ? `USER_WORKSPACE_DASHBOARD_${workspaceSlug}_${month}` : null, workspaceSlug ? () => userStore.fetchUserDashboardInfo(workspaceSlug.toString(), month) : null ); - - const isEditingAllowed = !!userStore.currentProjectRole && userStore.currentProjectRole >= EUserWorkspaceRoles.MEMBER; + const isEditingAllowed = !!userStore.currentWorkspaceRole && userStore.currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER; const handleTourCompleted = () => { userStore diff --git a/web/components/profile/overview/activity.tsx b/web/components/profile/overview/activity.tsx index 1bba6a25f..9512a98f2 100644 --- a/web/components/profile/overview/activity.tsx +++ b/web/components/profile/overview/activity.tsx @@ -1,7 +1,5 @@ import { useRouter } from "next/router"; - import useSWR from "swr"; - // services import { UserService } from "services/user.service"; // components @@ -9,7 +7,6 @@ import { ActivityMessage } from "components/core"; // ui import { ProfileEmptyState } from "components/ui"; import { Loader } from "@plane/ui"; -import { Rocket } from "lucide-react"; // image import recentActivityEmptyState from "public/empty-state/recent_activity.svg"; // helpers @@ -67,10 +64,9 @@ export const ProfileActivity = () => { href={`/${workspaceSlug}/projects/${activity.project}/issues/${activity.issue}`} target="_blank" rel="noopener noreferrer" - className="inline-flex items-center gap-1 font-medium text-custom-text-100 hover:underline" + className="inline-flex items-center gap-1 font-medium text-custom-text-200 hover:underline" > - Issue - + Issue. )} diff --git a/web/components/profile/sidebar.tsx b/web/components/profile/sidebar.tsx index 6c01db9f7..79cc96b5a 100644 --- a/web/components/profile/sidebar.tsx +++ b/web/components/profile/sidebar.tsx @@ -89,8 +89,8 @@ export const ProfileSidebar = () => { className="h-full w-full rounded object-cover" /> ) : ( -
- {userProjectsData.user_data.display_name?.[0]} +
+ {userProjectsData.user_data.first_name?.[0]}
)}
@@ -149,8 +149,8 @@ export const ProfileSidebar = () => { completedIssuePercentage <= 35 ? "bg-red-500/10 text-red-500" : completedIssuePercentage <= 70 - ? "bg-yellow-500/10 text-yellow-500" - : "bg-green-500/10 text-green-500" + ? "bg-yellow-500/10 text-yellow-500" + : "bg-green-500/10 text-green-500" }`} > {completedIssuePercentage}% diff --git a/web/components/project/card-list.tsx b/web/components/project/card-list.tsx index 76f3112b6..3ccc403c1 100644 --- a/web/components/project/card-list.tsx +++ b/web/components/project/card-list.tsx @@ -23,12 +23,11 @@ export const ProjectCardList: FC = observer((props) => { project: projectStore, commandPalette: commandPaletteStore, trackEvent: { setTrackElement }, - user: { currentProjectRole }, + user: { currentWorkspaceRole }, } = useMobxStore(); const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : null; - - const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER; + const isEditingAllowed = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER; if (!projects) { return ( diff --git a/web/components/project/create-project-modal.tsx b/web/components/project/create-project-modal.tsx index 83c7851b9..2207bb898 100644 --- a/web/components/project/create-project-modal.tsx +++ b/web/components/project/create-project-modal.tsx @@ -182,7 +182,7 @@ export const CreateProjectModal: FC = observer((props) => { setValue( "identifier", e.target.value - .replace(/[^a-zA-Z0-9]/g, "") + .replace(/[^ÇŞĞIİÖÜA-Za-z0-9]/g, "") .toUpperCase() .substring(0, 5) ); @@ -191,7 +191,7 @@ export const CreateProjectModal: FC = observer((props) => { const handleIdentifierChange = (onChange: any) => (e: ChangeEvent) => { const { value } = e.target; - const alphanumericValue = value.replace(/[^a-zA-Z0-9]/g, ""); + const alphanumericValue = value.replace(/[^ÇŞĞIİÖÜA-Za-z0-9]/g, ""); setIsChangeInIdentifierRequired(false); onChange(alphanumericValue.toUpperCase()); }; @@ -301,8 +301,9 @@ export const CreateProjectModal: FC = observer((props) => { name="identifier" rules={{ required: "Identifier is required", + // allow only alphanumeric & non-latin characters validate: (value) => - /^[A-Z0-9]+$/.test(value.toUpperCase()) || "Identifier must be in uppercase.", + /^[ÇŞĞIİÖÜA-Z0-9]+$/.test(value.toUpperCase()) || "Identifier must be in uppercase.", minLength: { value: 1, message: "Identifier must at least be of 1 character", diff --git a/web/components/project/sidebar-list-item.tsx b/web/components/project/sidebar-list-item.tsx index 4ed1bf2c4..56cc6130d 100644 --- a/web/components/project/sidebar-list-item.tsx +++ b/web/components/project/sidebar-list-item.tsx @@ -253,32 +253,31 @@ export const ProjectSidebarListItem: React.FC = observer((props) => { )} {project.archive_in > 0 && ( - router.push(`/${workspaceSlug}/projects/${project?.id}/archived-issues/`)} - > -
- - Archived Issues -
+ + +
+ + Archived Issues +
+
)} - router.push(`/${workspaceSlug}/projects/${project?.id}/draft-issues`)} - > -
- - Draft Issues -
+ + +
+ + Draft Issues +
+
- router.push(`/${workspaceSlug}/projects/${project?.id}/settings`)} - > -
- - Settings -
+ + +
+ + Settings +
+
- {/* leave project */} {isViewerOrGuest && ( diff --git a/web/components/project/sidebar-list.tsx b/web/components/project/sidebar-list.tsx index 6c9054ac0..9a71409d3 100644 --- a/web/components/project/sidebar-list.tsx +++ b/web/components/project/sidebar-list.tsx @@ -138,9 +138,9 @@ export const ProjectSidebarList: FC = observer(() => { > Favorites {open ? ( - + ) : ( - + )} {isAuthorizedUser && ( @@ -215,9 +215,9 @@ export const ProjectSidebarList: FC = observer(() => { > Projects {open ? ( - + ) : ( - + )} {isAuthorizedUser && ( diff --git a/web/components/web-hooks/create-webhook-modal.tsx b/web/components/web-hooks/create-webhook-modal.tsx new file mode 100644 index 000000000..5bb8a2c76 --- /dev/null +++ b/web/components/web-hooks/create-webhook-modal.tsx @@ -0,0 +1,139 @@ +import React, { useState } from "react"; +import { useRouter } from "next/router"; +import { Dialog, Transition } from "@headlessui/react"; +// components +import { WebhookForm } from "./form"; +import { GeneratedHookDetails } from "./generated-hook-details"; +// hooks +import useToast from "hooks/use-toast"; +// helpers +import { csvDownload } from "helpers/download.helper"; +// utils +import { getCurrentHookAsCSV } from "./utils"; +// types +import { IWebhook, IWorkspace, TWebhookEventTypes } from "types"; + +interface WebhookWithKey { + webHook: IWebhook; + secretKey: string | undefined; +} +interface ICreateWebhookModal { + currentWorkspace: IWorkspace | null; + isOpen: boolean; + clearSecretKey: () => void; + createWebhook: (workspaceSlug: string, data: Partial) => Promise; + onClose: () => void; +} + +export const CreateWebhookModal: React.FC = (props) => { + const { isOpen, onClose, currentWorkspace, createWebhook, clearSecretKey } = props; + // states + const [generatedWebhook, setGeneratedKey] = useState(null); + // router + const router = useRouter(); + const { workspaceSlug } = router.query; + // toast + const { setToastAlert } = useToast(); + + const handleCreateWebhook = async (formData: IWebhook, webhookEventType: TWebhookEventTypes) => { + if (!workspaceSlug) return; + + let payload: Partial = { + url: formData.url, + }; + + if (webhookEventType === "all") + payload = { + ...payload, + project: true, + cycle: true, + module: true, + issue: true, + issue_comment: true, + }; + else + payload = { + ...payload, + project: formData.project ?? false, + cycle: formData.cycle ?? false, + module: formData.module ?? false, + issue: formData.issue ?? false, + issue_comment: formData.issue_comment ?? false, + }; + + await createWebhook(workspaceSlug.toString(), payload) + .then(({ webHook, secretKey }) => { + setToastAlert({ + type: "success", + title: "Success!", + message: "Webhook created successfully.", + }); + + setGeneratedKey(webHook); + + const csvData = getCurrentHookAsCSV(currentWorkspace, webHook, secretKey); + csvDownload(csvData, `webhook-secret-key-${Date.now()}`); + }) + .catch((error) => { + setToastAlert({ + type: "error", + title: "Error!", + message: error?.error ?? "Something went wrong. Please try again.", + }); + }); + }; + + const handleClose = () => { + onClose(); + setTimeout(() => { + clearSecretKey(); + setGeneratedKey(null); + }, 350); + }; + + return ( + + { + if (!generatedWebhook) handleClose(); + }} + > + +
+ + +
+
+ + + {!generatedWebhook ? ( + + ) : ( + + )} + + +
+
+
+
+ ); +}; diff --git a/web/components/web-hooks/empty-state.tsx b/web/components/web-hooks/empty-state.tsx index 234a4d0a2..015cf57bc 100644 --- a/web/components/web-hooks/empty-state.tsx +++ b/web/components/web-hooks/empty-state.tsx @@ -1,14 +1,16 @@ -// next -import { useRouter } from "next/router"; +import React from "react"; import Image from "next/image"; // ui import { Button } from "@plane/ui"; // assets import EmptyWebhook from "public/empty-state/web-hook.svg"; -export const WebhooksEmptyState = () => { - const router = useRouter(); +type Props = { + onClick: () => void; +}; +export const WebhooksEmptyState: React.FC = (props) => { + const { onClick } = props; return (
{

Create webhooks to receive real-time updates and automate actions

-
diff --git a/web/components/web-hooks/form/form.tsx b/web/components/web-hooks/form/form.tsx index 43600b04a..4d98c2b7f 100644 --- a/web/components/web-hooks/form/form.tsx +++ b/web/components/web-hooks/form/form.tsx @@ -1,11 +1,8 @@ import React, { FC, useEffect, useState } from "react"; -import { useRouter } from "next/router"; import { Controller, useForm } from "react-hook-form"; import { observer } from "mobx-react-lite"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; -// hooks -import useToast from "hooks/use-toast"; // components import { WebhookIndividualEventOptions, @@ -13,17 +10,16 @@ import { WebhookOptions, WebhookSecretKey, WebhookToggle, - getCurrentHookAsCSV, } from "components/web-hooks"; // ui import { Button } from "@plane/ui"; -// helpers -import { csvDownload } from "helpers/download.helper"; // types import { IWebhook, TWebhookEventTypes } from "types"; type Props = { data?: Partial; + onSubmit: (data: IWebhook, webhookEventType: TWebhookEventTypes) => Promise; + handleClose?: () => void; }; const initialWebhookPayload: Partial = { @@ -36,18 +32,12 @@ const initialWebhookPayload: Partial = { }; export const WebhookForm: FC = observer((props) => { - const { data } = props; + const { data, onSubmit, handleClose } = props; // states const [webhookEventType, setWebhookEventType] = useState("all"); - // router - const router = useRouter(); - const { workspaceSlug } = router.query; - // toast - const { setToastAlert } = useToast(); // mobx store const { - webhook: { createWebhook, updateWebhook }, - workspace: { currentWorkspace }, + webhook: { webhookSecretKey }, } = useMobxStore(); // use form const { @@ -58,74 +48,8 @@ export const WebhookForm: FC = observer((props) => { defaultValues: { ...initialWebhookPayload, ...data }, }); - const handleCreateWebhook = async (formData: IWebhook) => { - if (!workspaceSlug) return; - - let payload: Partial = { - url: formData.url, - }; - - if (webhookEventType === "all") - payload = { - ...payload, - project: true, - cycle: true, - module: true, - issue: true, - issue_comment: true, - }; - else - payload = { - ...payload, - project: formData.project ?? false, - cycle: formData.cycle ?? false, - module: formData.module ?? false, - issue: formData.issue ?? false, - issue_comment: formData.issue_comment ?? false, - }; - - await createWebhook(workspaceSlug.toString(), payload) - .then(({ webHook, secretKey }) => { - setToastAlert({ - type: "success", - title: "Success!", - message: "Webhook created successfully.", - }); - - const csvData = getCurrentHookAsCSV(currentWorkspace, webHook, secretKey); - csvDownload(csvData, `webhook-secret-key-${Date.now()}`); - - if (webHook && webHook.id) - router.push({ pathname: `/${workspaceSlug}/settings/webhooks/${webHook.id}`, query: { isCreated: true } }); - }) - .catch((error) => { - setToastAlert({ - type: "error", - title: "Error!", - message: error?.error ?? "Something went wrong. Please try again.", - }); - }); - }; - - const handleUpdateWebhook = async (formData: IWebhook) => { - if (!workspaceSlug || !data || !data.id) return; - - const payload = { - url: formData?.url, - is_active: formData?.is_active, - project: formData?.project, - cycle: formData?.cycle, - module: formData?.module, - issue: formData?.issue, - issue_comment: formData?.issue_comment, - }; - - return await updateWebhook(workspaceSlug.toString(), data.id, payload); - }; - const handleFormSubmit = async (formData: IWebhook) => { - if (data) await handleUpdateWebhook(formData); - else await handleCreateWebhook(formData); + await onSubmit(formData, webhookEventType); }; useEffect(() => { @@ -161,12 +85,26 @@ export const WebhookForm: FC = observer((props) => {
{webhookEventType === "individual" && }
-
- {data && } - -
+ {data ? ( +
+ + + +
+ ) : ( +
+ + {!webhookSecretKey && ( + + )} +
+ )}
); diff --git a/web/components/web-hooks/form/secret-key.tsx b/web/components/web-hooks/form/secret-key.tsx index eecc7aa71..e3cc6e45f 100644 --- a/web/components/web-hooks/form/secret-key.tsx +++ b/web/components/web-hooks/form/secret-key.tsx @@ -56,11 +56,11 @@ export const WebhookSecretKey: FC = observer((props) => { }; const handleRegenerateSecretKey = () => { - if (!workspaceSlug || !webhookId) return; + if (!workspaceSlug || !data.id) return; setIsRegenerating(true); - regenerateSecretKey(workspaceSlug.toString(), webhookId.toString()) + regenerateSecretKey(workspaceSlug.toString(), data.id) .then(() => { setToastAlert({ type: "success", @@ -92,10 +92,10 @@ export const WebhookSecretKey: FC = observer((props) => { <> {(data || webhookSecretKey) && (
-
Secret key
+ {webhookId &&
Secret key
}
Generate a token to sign-in to the webhook payload
-
+
{shouldShowKey ? (

{webhookSecretKey}

diff --git a/web/components/web-hooks/generated-hook-details.tsx b/web/components/web-hooks/generated-hook-details.tsx new file mode 100644 index 000000000..60dfeda30 --- /dev/null +++ b/web/components/web-hooks/generated-hook-details.tsx @@ -0,0 +1,33 @@ +// components +import { WebhookSecretKey } from "./form"; +// ui +import { Button } from "@plane/ui"; +// types +import { IWebhook } from "types"; + +type Props = { + handleClose: () => void; + webhookDetails: IWebhook; +}; + +export const GeneratedHookDetails: React.FC = (props) => { + const { handleClose, webhookDetails } = props; + + return ( +
+
+

Key created

+

+ Copy and save this secret key in Plane Pages. You can{"'"}t see this key after you hit Close. A CSV file + containing the key has been downloaded. +

+
+ +
+ +
+
+ ); +}; diff --git a/web/components/web-hooks/index.ts b/web/components/web-hooks/index.ts index 79fe4a520..fd23f4330 100644 --- a/web/components/web-hooks/index.ts +++ b/web/components/web-hooks/index.ts @@ -1,6 +1,8 @@ -export * from "./form"; export * from "./delete-webhook-modal"; export * from "./empty-state"; +export * from "./form"; +export * from "./generated-hook-details"; export * from "./utils"; +export * from "./create-webhook-modal"; export * from "./webhooks-list-item"; export * from "./webhooks-list"; diff --git a/web/components/workspace/issues-stats.tsx b/web/components/workspace/issues-stats.tsx index 1fa5cf2df..b8cc8432f 100644 --- a/web/components/workspace/issues-stats.tsx +++ b/web/components/workspace/issues-stats.tsx @@ -7,6 +7,7 @@ import { Info } from "lucide-react"; // types import { IUserWorkspaceDashboard } from "types"; import { useRouter } from "next/router"; +import Link from "next/link"; type Props = { data: IUserWorkspaceDashboard | undefined; @@ -19,61 +20,40 @@ export const IssuesStats: React.FC = ({ data }) => {
-
-

Issues assigned to you

-
- {data ? ( -
router.push(`/${workspaceSlug}/workspace-views/assigned`)} - > - {data.assigned_issues_count} -
- ) : ( - - - - )} -
-
-
-

Pending issues

-
- {data ? ( - data.pending_issues_count - ) : ( - - - - )} -
-
+ +
+

Issues assigned to you

+
+
{data?.assigned_issues_count}
+
+
+ + +
+

Pending issues

+
{data?.pending_issues_count}
+
+
-
-

Completed issues

-
- {data ? ( - data.completed_issues_count - ) : ( - - - - )} -
-
-
-

Issues due by this week

-
- {data ? ( - data.issues_due_week_count - ) : ( - - - - )} -
-
+ +
+

Completed issues

+
{data?.completed_issues_count}
+
+ + +
+

Issues due by this week

+
{data?.issues_due_week_count}
+
+
diff --git a/web/components/workspace/send-workspace-invitation-modal.tsx b/web/components/workspace/send-workspace-invitation-modal.tsx index a720e31f5..43609d1c5 100644 --- a/web/components/workspace/send-workspace-invitation-modal.tsx +++ b/web/components/workspace/send-workspace-invitation-modal.tsx @@ -68,6 +68,12 @@ export const SendWorkspaceInvitationModal: React.FC = observer((props) => append({ email: "", role: 15 }); }; + const onSubmitForm = async (data: FormValues) => { + await onSubmit(data)?.then(() => { + reset(defaultValues); + }); + }; + useEffect(() => { if (fields.length === 0) append([{ email: "", role: 15 }]); }, [fields, append]); @@ -100,7 +106,7 @@ export const SendWorkspaceInvitationModal: React.FC = observer((props) => >
{ if (e.code === "Enter") e.preventDefault(); }} diff --git a/web/components/workspace/settings/members-list-item.tsx b/web/components/workspace/settings/members-list-item.tsx index b869d4d63..bd650c888 100644 --- a/web/components/workspace/settings/members-list-item.tsx +++ b/web/components/workspace/settings/members-list-item.tsx @@ -28,7 +28,8 @@ type Props = { display_name: string; role: TUserWorkspaceRole; status: boolean; - member: boolean; + is_member: boolean; + responded_at: string | null; accountCreated: boolean; }; }; @@ -102,9 +103,8 @@ export const WorkspaceMembersListItem: FC = observer((props) => { }; const handleRemove = async () => { - if (member.member) { + if (member.is_member) { const memberId = member.memberId; - if (memberId === currentUser?.id) await handleLeaveWorkspace(); else await handleRemoveMember(); } else await handleRemoveInvitation(); @@ -154,7 +154,7 @@ export const WorkspaceMembersListItem: FC = observer((props) => { )}
- {member.member ? ( + {member.is_member ? ( {member.first_name} {member.last_name} @@ -175,16 +175,21 @@ export const WorkspaceMembersListItem: FC = observer((props) => {
- {!member?.status && ( + {!member?.status && !member.responded_at && (

Pending

)} - {member?.status && !member?.accountCreated && ( + {member?.status && !member.is_member && (

Account not created

)} + {!member?.status && member.responded_at && ( +
+

Rejected

+
+ )} diff --git a/web/components/workspace/sidebar-dropdown.tsx b/web/components/workspace/sidebar-dropdown.tsx index 8d1cfa27d..cc3c878e4 100644 --- a/web/components/workspace/sidebar-dropdown.tsx +++ b/web/components/workspace/sidebar-dropdown.tsx @@ -16,7 +16,7 @@ import { Avatar, Loader } from "@plane/ui"; import { IWorkspace } from "types"; // Static Data -const userLinks = (workspaceSlug: string, userId: string) => [ +const WORKSPACE_DROPDOWN_ITEMS = (workspaceSlug: string, userId: string) => [ { name: "Workspace Settings", href: `/${workspaceSlug}/settings`, @@ -155,8 +155,8 @@ export const WorkspaceSidebarDropdown = observer(() => { workspaces.map((workspace: IWorkspace) => ( {() => ( - + )} )) @@ -198,17 +198,19 @@ export const WorkspaceSidebarDropdown = observer(() => {

No workspace found!

)}
- { - setTrackElement("APP_SIEDEBAR_WORKSPACE_DROPDOWN"); - router.push("/create-workspace"); - }} - className="flex w-full items-center gap-2 px-2 py-1 text-sm text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80" - > - - Create Workspace + + {() => ( + { + setTrackElement("APP_SIEDEBAR_WORKSPACE_DROPDOWN"); + }} + > + + Create Workspace + + )}
@@ -222,18 +224,20 @@ export const WorkspaceSidebarDropdown = observer(() => { )}
- {userLinks(workspaceSlug?.toString() ?? "", currentUser?.id ?? "").map((link, index) => ( - { - router.push(link.href); - }} - className="flex w-full cursor-pointer items-center justify-start rounded px-2 py-1 text-sm text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80" - > - {link.name} - - ))} + {WORKSPACE_DROPDOWN_ITEMS(workspaceSlug?.toString() ?? "", currentUser?.id ?? "").map( + (link, index) => ( + + {() => ( + + {link.name} + + )} + + ) + )}
{ } }; -export const getDateRangeStatus = (startDate: string | null | undefined, endDate: string | null | undefined) => { - if (!startDate || !endDate) return "draft"; - - const now = new Date(); - const start = new Date(startDate); - const end = new Date(endDate); - - if (start <= now && end >= now) return "current"; - else if (start > now) return "upcoming"; - else return "completed"; -}; - export const renderShortDateWithYearFormat = (date: string | Date, placeholder?: string) => { if (!date || date === "") return null; diff --git a/web/helpers/theme.helper.ts b/web/helpers/theme.helper.ts index 8a521bc31..16cd8cd79 100644 --- a/web/helpers/theme.helper.ts +++ b/web/helpers/theme.helper.ts @@ -60,6 +60,7 @@ const calculateShades = (hexValue: string): TShades => { }; export const applyTheme = (palette: string, isDarkPalette: boolean) => { + if (!palette) return; const dom = document?.querySelector("[data-theme='custom']"); // palette: [bg, text, primary, sidebarBg, sidebarText] const values: string[] = palette.split(","); diff --git a/web/hooks/use-sign-in-redirection.ts b/web/hooks/use-sign-in-redirection.ts index 1863e510e..25d4e8bbd 100644 --- a/web/hooks/use-sign-in-redirection.ts +++ b/web/hooks/use-sign-in-redirection.ts @@ -17,36 +17,54 @@ const useSignInRedirection = (): UseSignInRedirectionProps => { const [error, setError] = useState(null); // router const router = useRouter(); - const { next_url } = router.query; + const { next_path } = router.query; // mobx store const { user: { fetchCurrentUser, fetchCurrentUserSettings }, } = useMobxStore(); + const isValidURL = (url: string): boolean => { + const disallowedSchemes = /^(https?|ftp):\/\//i; + return !disallowedSchemes.test(url); + }; + + console.log("next_path", next_path); + const handleSignInRedirection = useCallback( async (user: IUser) => { - // if the user is not onboarded, redirect them to the onboarding page - if (!user.is_onboarded) { - router.push("/onboarding"); - return; - } - // if next_url is provided, redirect the user to that url - if (next_url) { - router.push(next_url.toString()); - return; - } + try { + // if the user is not onboarded, redirect them to the onboarding page + if (!user.is_onboarded) { + router.push("/onboarding"); + return; + } + // if next_path is provided, redirect the user to that url + if (next_path) { + if (isValidURL(next_path.toString())) { + router.push(next_path.toString()); + return; + } else { + router.push("/"); + return; + } + } - // if the user is onboarded, fetch their last workspace details - await fetchCurrentUserSettings() - .then((userSettings: IUserSettings) => { - const workspaceSlug = - userSettings?.workspace?.last_workspace_slug || userSettings?.workspace?.fallback_workspace_slug; - if (workspaceSlug) router.push(`/${workspaceSlug}`); - else router.push("/profile"); - }) - .catch((err) => setError(err)); + // Fetch the current user settings + const userSettings: IUserSettings = await fetchCurrentUserSettings(); + + // Extract workspace details + const workspaceSlug = + userSettings?.workspace?.last_workspace_slug || userSettings?.workspace?.fallback_workspace_slug; + + // Redirect based on workspace details or to profile if not available + if (workspaceSlug) router.push(`/${workspaceSlug}`); + else router.push("/profile"); + } catch (error) { + console.error("Error in handleSignInRedirection:", error); + setError(error); + } }, - [fetchCurrentUserSettings, router, next_url] + [fetchCurrentUserSettings, router, next_path] ); const updateUserInfo = useCallback(async () => { diff --git a/web/hooks/use-user-auth.tsx b/web/hooks/use-user-auth.tsx index 882c0b713..8290a4545 100644 --- a/web/hooks/use-user-auth.tsx +++ b/web/hooks/use-user-auth.tsx @@ -12,7 +12,7 @@ const workspaceService = new WorkspaceService(); const useUserAuth = (routeAuth: "sign-in" | "onboarding" | "admin" | null = "admin") => { const router = useRouter(); - const { next_url } = router.query; + const { next_path } = router.query; const [isRouteAccess, setIsRouteAccess] = useState(true); const { @@ -29,6 +29,11 @@ const useUserAuth = (routeAuth: "sign-in" | "onboarding" | "admin" | null = "adm shouldRetryOnError: false, }); + const isValidURL = (url: string): boolean => { + const disallowedSchemes = /^(https?|ftp):\/\//i; + return !disallowedSchemes.test(url); + }; + useEffect(() => { const handleWorkSpaceRedirection = async () => { workspaceService.userWorkspaces().then(async (userWorkspaces) => { @@ -84,8 +89,15 @@ const useUserAuth = (routeAuth: "sign-in" | "onboarding" | "admin" | null = "adm if (!isLoading) { setIsRouteAccess(() => true); if (user) { - if (next_url) router.push(next_url.toString()); - else handleUserRouteAuthentication(); + if (next_path) { + if (isValidURL(next_path.toString())) { + router.push(next_path.toString()); + return; + } else { + router.push("/"); + return; + } + } else handleUserRouteAuthentication(); } else { if (routeAuth === "sign-in") { setIsRouteAccess(() => false); @@ -97,7 +109,7 @@ const useUserAuth = (routeAuth: "sign-in" | "onboarding" | "admin" | null = "adm } } } - }, [user, isLoading, routeAuth, router, next_url]); + }, [user, isLoading, routeAuth, router, next_path]); return { isLoading: isRouteAccess, diff --git a/web/hooks/use-user.tsx b/web/hooks/use-user.tsx index 97518fed6..2203faaa2 100644 --- a/web/hooks/use-user.tsx +++ b/web/hooks/use-user.tsx @@ -31,7 +31,7 @@ export default function useUser({ redirectTo = "", redirectIfFound = false, opti ) { router.push(redirectTo); return; - // const nextLocation = router.asPath.split("?next=")[1]; + // const nextLocation = router.asPath.split("?next_path=")[1]; // if (nextLocation) { // router.push(nextLocation as string); // return; diff --git a/web/layouts/auth-layout/user-wrapper.tsx b/web/layouts/auth-layout/user-wrapper.tsx index 50b17fdb7..ccc30a382 100644 --- a/web/layouts/auth-layout/user-wrapper.tsx +++ b/web/layouts/auth-layout/user-wrapper.tsx @@ -56,7 +56,7 @@ export const UserAuthWrapper: FC = observer((props) => { if (currentUserError) { const redirectTo = router.asPath; - router.push(`/?next=${redirectTo}`); + router.push(`/?next_path=${redirectTo}`); return null; } diff --git a/web/lib/wrappers/posthog-wrapper.tsx b/web/lib/wrappers/posthog-wrapper.tsx index 1e7c35c0d..9c17ee2bd 100644 --- a/web/lib/wrappers/posthog-wrapper.tsx +++ b/web/lib/wrappers/posthog-wrapper.tsx @@ -47,8 +47,7 @@ const PosthogWrapper: FC = (props) => { capture_pageview: false, // Disable automatic pageview capture, as we capture manually }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [posthogAPIKey, posthogHost]); useEffect(() => { // Track page views @@ -60,7 +59,7 @@ const PosthogWrapper: FC = (props) => { return () => { router.events.off("routeChangeComplete", handleRouteChange); }; - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); if (posthogAPIKey) { diff --git a/web/next.config.js b/web/next.config.js index 29925a312..e018ea317 100644 --- a/web/next.config.js +++ b/web/next.config.js @@ -2,6 +2,14 @@ require("dotenv").config({ path: ".env" }); const { withSentryConfig } = require("@sentry/nextjs"); const nextConfig = { + async headers() { + return [ + { + source: "/(.*)?", + headers: [{ key: "X-Frame-Options", value: "SAMEORIGIN" }], + }, + ]; + }, reactStrictMode: false, swcMinify: true, images: { diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/pages/[pageId].tsx b/web/pages/[workspaceSlug]/projects/[projectId]/pages/[pageId].tsx index a781cfd78..f91be2c2d 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/pages/[pageId].tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/pages/[pageId].tsx @@ -177,7 +177,7 @@ const PageDetailsPage: NextPageWithLayout = observer(() => { onErrorAction: () => void ) => { const commonSwrOptions: MutatorOptions = { - revalidate: true, + revalidate: false, populateCache: false, rollbackOnError: () => { onErrorAction(); @@ -201,6 +201,22 @@ const PageDetailsPage: NextPageWithLayout = observer(() => { }); }; + useEffect(() => { + mutatePageDetails(undefined, { + revalidate: true, + populateCache: true, + rollbackOnError: () => { + actionCompleteAlert({ + title: `Page could not be updated`, + message: `Sorry, page could not be updated, please try again later`, + type: "error", + }); + return true; + }, + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const updatePage = async (formData: IPage) => { if (!workspaceSlug || !projectId || !pageId) return; @@ -246,6 +262,11 @@ const PageDetailsPage: NextPageWithLayout = observer(() => { // ================ Page Menu Actions ================== const duplicate_page = async () => { const currentPageValues = getValues(); + + if (!currentPageValues?.description_html) { + currentPageValues.description_html = pageDetails?.description_html as string; + } + const formData: Partial = { name: "Copy of " + pageDetails?.name, description_html: currentPageValues.description_html, @@ -336,7 +357,7 @@ const PageDetailsPage: NextPageWithLayout = observer(() => { debounce(async () => { handleSubmit(updatePage)().finally(() => setIsSubmitting("submitted")); }, 1500), - [handleSubmit] + [handleSubmit, pageDetails] ); if (error) diff --git a/web/pages/[workspaceSlug]/settings/webhooks/[webhookId].tsx b/web/pages/[workspaceSlug]/settings/webhooks/[webhookId].tsx index b5e4ff64a..c8b5d2cce 100644 --- a/web/pages/[workspaceSlug]/settings/webhooks/[webhookId].tsx +++ b/web/pages/[workspaceSlug]/settings/webhooks/[webhookId].tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useState } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import useSWR from "swr"; @@ -7,6 +7,8 @@ import { useMobxStore } from "lib/mobx/store-provider"; // layouts import { AppLayout } from "layouts/app-layout"; import { WorkspaceSettingLayout } from "layouts/settings-layout"; +// hooks +import useToast from "hooks/use-toast"; // components import { WorkspaceSettingHeader } from "components/headers"; import { DeleteWebhookModal, WebhookDeleteSection, WebhookForm } from "components/web-hooks"; @@ -14,22 +16,21 @@ import { DeleteWebhookModal, WebhookDeleteSection, WebhookForm } from "component import { Spinner } from "@plane/ui"; // types import { NextPageWithLayout } from "types/app"; +import { IWebhook } from "types"; const WebhookDetailsPage: NextPageWithLayout = observer(() => { // states const [deleteWebhookModal, setDeleteWebhookModal] = useState(false); // router const router = useRouter(); - const { workspaceSlug, webhookId, isCreated } = router.query; + const { workspaceSlug, webhookId } = router.query; // mobx store const { - webhook: { currentWebhook, clearSecretKey, fetchWebhookById }, + webhook: { currentWebhook, fetchWebhookById, updateWebhook }, user: { currentWorkspaceRole }, } = useMobxStore(); - - useEffect(() => { - if (isCreated !== "true") clearSecretKey(); - }, [clearSecretKey, isCreated]); + // toast + const { setToastAlert } = useToast(); const isAdmin = currentWorkspaceRole === 20; @@ -40,6 +41,34 @@ const WebhookDetailsPage: NextPageWithLayout = observer(() => { : null ); + const handleUpdateWebhook = async (formData: IWebhook) => { + if (!workspaceSlug || !formData || !formData.id) return; + const payload = { + url: formData?.url, + is_active: formData?.is_active, + project: formData?.project, + cycle: formData?.cycle, + module: formData?.module, + issue: formData?.issue, + issue_comment: formData?.issue_comment, + }; + await updateWebhook(workspaceSlug.toString(), formData.id, payload) + .then(() => { + setToastAlert({ + type: "success", + title: "Success!", + message: "Webhook updated successfully.", + }); + }) + .catch((error) => { + setToastAlert({ + type: "error", + title: "Error!", + message: error?.error ?? "Something went wrong. Please try again.", + }); + }); + }; + if (!isAdmin) return (
@@ -58,7 +87,7 @@ const WebhookDetailsPage: NextPageWithLayout = observer(() => { <> setDeleteWebhookModal(false)} />
- + await handleUpdateWebhook(data)} data={currentWebhook} /> {currentWebhook && setDeleteWebhookModal(true)} />}
diff --git a/web/pages/[workspaceSlug]/settings/webhooks/create.tsx b/web/pages/[workspaceSlug]/settings/webhooks/create.tsx deleted file mode 100644 index 6615d4c68..000000000 --- a/web/pages/[workspaceSlug]/settings/webhooks/create.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from "react"; -import { observer } from "mobx-react-lite"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; -// layouts -import { AppLayout } from "layouts/app-layout"; -import { WorkspaceSettingLayout } from "layouts/settings-layout"; -// components -import { WorkspaceSettingHeader } from "components/headers"; -import { WebhookForm } from "components/web-hooks"; -// types -import { NextPageWithLayout } from "types/app"; - -const CreateWebhookPage: NextPageWithLayout = observer(() => { - const { - user: { currentWorkspaceRole }, - } = useMobxStore(); - - const isAdmin = currentWorkspaceRole === 20; - - if (!isAdmin) - return ( -
-

You are not authorized to access this page.

-
- ); - - return ( -
- -
- ); -}); - -CreateWebhookPage.getLayout = function getLayout(page: React.ReactElement) { - return ( - }> - {page} - - ); -}; - -export default CreateWebhookPage; diff --git a/web/pages/[workspaceSlug]/settings/webhooks/index.tsx b/web/pages/[workspaceSlug]/settings/webhooks/index.tsx index 0fe6e4e71..407c3ebab 100644 --- a/web/pages/[workspaceSlug]/settings/webhooks/index.tsx +++ b/web/pages/[workspaceSlug]/settings/webhooks/index.tsx @@ -1,5 +1,4 @@ -import React from "react"; -import Link from "next/link"; +import React, { useEffect, useState } from "react"; import { useRouter } from "next/router"; import useSWR from "swr"; import { observer } from "mobx-react-lite"; @@ -10,18 +9,22 @@ import { AppLayout } from "layouts/app-layout"; import { WorkspaceSettingLayout } from "layouts/settings-layout"; // components import { WorkspaceSettingHeader } from "components/headers"; -import { WebhooksList, WebhooksEmptyState } from "components/web-hooks"; +import { WebhooksList, WebhooksEmptyState, CreateWebhookModal } from "components/web-hooks"; // ui import { Button, Spinner } from "@plane/ui"; // types import { NextPageWithLayout } from "types/app"; const WebhooksListPage: NextPageWithLayout = observer(() => { + // states + const [showCreateWebhookModal, setShowCreateWebhookModal] = useState(false); + // router const router = useRouter(); const { workspaceSlug } = router.query; const { - webhook: { fetchWebhooks, webhooks }, + webhook: { fetchWebhooks, createWebhook, clearSecretKey, webhooks, webhookSecretKey }, + workspace: { currentWorkspace }, user: { currentWorkspaceRole }, } = useMobxStore(); @@ -32,6 +35,11 @@ const WebhooksListPage: NextPageWithLayout = observer(() => { workspaceSlug && isAdmin ? () => fetchWebhooks(workspaceSlug.toString()) : null ); + // clear secret key when modal is closed. + useEffect(() => { + if (!showCreateWebhookModal && webhookSecretKey) clearSecretKey(); + }, [showCreateWebhookModal, webhookSecretKey, clearSecretKey]); + if (!isAdmin) return (
@@ -48,21 +56,28 @@ const WebhooksListPage: NextPageWithLayout = observer(() => { return (
+ { + setShowCreateWebhookModal(false); + }} + /> {Object.keys(webhooks).length > 0 ? (
Webhooks
- - - +
) : (
- + setShowCreateWebhookModal(true)} />
)}
diff --git a/web/pages/_app.tsx b/web/pages/_app.tsx index c6fb89772..e8c10812a 100644 --- a/web/pages/_app.tsx +++ b/web/pages/_app.tsx @@ -3,8 +3,6 @@ import Head from "next/head"; import { AppProps } from "next/app"; // styles import "styles/globals.css"; -import "styles/editor.css"; -import "styles/table.css"; import "styles/command-pallette.css"; import "styles/nprogress.css"; import "styles/react-datepicker.css"; diff --git a/web/pages/accounts/password.tsx b/web/pages/accounts/password.tsx index 9ea99f2b3..b5683d210 100644 --- a/web/pages/accounts/password.tsx +++ b/web/pages/accounts/password.tsx @@ -108,7 +108,7 @@ const HomePage: NextPageWithLayout = () => { onChange={onChange} ref={ref} hasError={Boolean(errors.email)} - placeholder="orville.wright@firstflight.com" + placeholder="orville.wright@frstflt.com" className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 text-onboarding-text-400" disabled /> diff --git a/web/pages/onboarding/index.tsx b/web/pages/onboarding/index.tsx index f6df0c4fc..e695b1f4c 100644 --- a/web/pages/onboarding/index.tsx +++ b/web/pages/onboarding/index.tsx @@ -11,8 +11,6 @@ import { Controller, useForm } from "react-hook-form"; import { useMobxStore } from "lib/mobx/store-provider"; // services import { WorkspaceService } from "services/workspace.service"; -// hooks -import useUserAuth from "hooks/use-user-auth"; // layouts import DefaultLayout from "layouts/default-layout"; import { UserAuthWrapper } from "layouts/auth-layout"; @@ -45,8 +43,6 @@ const OnboardingPage: NextPageWithLayout = observer(() => { const { setTheme } = useTheme(); - const {} = useUserAuth("onboarding"); - const { control, setValue } = useForm<{ full_name: string }>({ defaultValues: { full_name: "", @@ -158,8 +154,8 @@ const OnboardingPage: NextPageWithLayout = observer(() => { currentUser?.first_name ? `${currentUser?.first_name} ${currentUser?.last_name ?? ""}` : value.length > 0 - ? value - : currentUser?.email + ? value + : currentUser?.email } src={currentUser?.avatar} size={35} @@ -174,8 +170,8 @@ const OnboardingPage: NextPageWithLayout = observer(() => { {currentUser?.first_name ? `${currentUser?.first_name} ${currentUser?.last_name ?? ""}` : value.length > 0 - ? value - : null} + ? value + : null}

)} diff --git a/web/pages/workspace-invitations/index.tsx b/web/pages/workspace-invitations/index.tsx index 32cb61432..d374549aa 100644 --- a/web/pages/workspace-invitations/index.tsx +++ b/web/pages/workspace-invitations/index.tsx @@ -52,6 +52,19 @@ const WorkspaceInvitationPage: NextPageWithLayout = () => { .catch((err) => console.error(err)); }; + const handleReject = () => { + if (!invitationDetail) return; + workspaceService + .joinWorkspace(invitationDetail.workspace.slug, invitationDetail.id, { + accepted: false, + email: invitationDetail.email, + }) + .then(() => { + router.push("/"); + }) + .catch((err) => console.error(err)); + }; + return (
{invitationDetail ? ( @@ -77,13 +90,7 @@ const WorkspaceInvitationPage: NextPageWithLayout = () => { description="Your workspace is where you'll create projects, collaborate on your issues, and organize different streams of work in your Plane account." > - { - router.push("/"); - }} - /> + )} diff --git a/web/store/cycle/cycles.store.ts b/web/store/cycle/cycles.store.ts index 96122ec14..b6602172d 100644 --- a/web/store/cycle/cycles.store.ts +++ b/web/store/cycle/cycles.store.ts @@ -7,7 +7,6 @@ import { RootStore } from "../root"; import { ProjectService } from "services/project"; import { IssueService } from "services/issue"; import { CycleService } from "services/cycle.service"; -import { getDateRangeStatus } from "helpers/date-time.helper"; export interface ICycleStore { loader: boolean; @@ -318,7 +317,7 @@ export class CycleStore implements ICycleStore { }; addCycleToFavorites = async (workspaceSlug: string, projectId: string, cycle: ICycle) => { - const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date); + const cycleStatus = cycle.status; const statusCyclesList = this.cycles[projectId]?.[cycleStatus] ?? []; const allCyclesList = this.projectCycles ?? []; @@ -379,7 +378,7 @@ export class CycleStore implements ICycleStore { }; removeCycleFromFavorites = async (workspaceSlug: string, projectId: string, cycle: ICycle) => { - const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date); + const cycleStatus = cycle.status; const statusCyclesList = this.cycles[projectId]?.[cycleStatus] ?? []; const allCyclesList = this.projectCycles ?? []; diff --git a/web/store/workspace/workspace-member.store.ts b/web/store/workspace/workspace-member.store.ts index e699cb467..dc6183245 100644 --- a/web/store/workspace/workspace-member.store.ts +++ b/web/store/workspace/workspace-member.store.ts @@ -114,8 +114,8 @@ export class WorkspaceMemberStore implements IWorkspaceMemberStore { display_name: item.email, role: item.role, status: item.accepted, - member: false, - accountCreated: item.accepted, + is_member: false, + responded_at: item.responded_at, })) || []), ...(this.workspaceMembers?.map((item) => ({ id: item.id, @@ -127,8 +127,8 @@ export class WorkspaceMemberStore implements IWorkspaceMemberStore { display_name: item.member?.display_name, role: item.role, status: true, - member: true, - accountCreated: true, + is_member: true, + responded_at: "accepted", })) || []), ]; } diff --git a/web/styles/editor.css b/web/styles/editor.css deleted file mode 100644 index 149896c52..000000000 --- a/web/styles/editor.css +++ /dev/null @@ -1,333 +0,0 @@ -.ProseMirror p.is-editor-empty:first-child::before { - content: attr(data-placeholder); - float: left; - color: rgb(var(--color-text-400)); - pointer-events: none; - height: 0; -} - -/* block quotes */ -.ProseMirror blockquote p::before, -.ProseMirror blockquote p::after { - display: none; -} - -.ProseMirror .is-empty::before { - content: attr(data-placeholder); - float: left; - color: rgb(var(--color-text-400)); - pointer-events: none; - height: 0; -} - -/* Custom image styles */ - -.ProseMirror img { - transition: filter 0.1s ease-in-out; - - &:hover { - cursor: pointer; - filter: brightness(90%); - } - - &.ProseMirror-selectednode { - outline: 3px solid #5abbf7; - filter: brightness(90%); - } -} - -.ProseMirror-gapcursor:after { - border-top: 1px solid rgb(var(--color-text-100)) !important; -} - -/* Custom TODO list checkboxes – shoutout to this awesome tutorial: https://moderncss.dev/pure-css-custom-checkbox-style/ */ - -ul[data-type="taskList"] li > label { - margin-right: 0.2rem; - user-select: none; -} - -@media screen and (max-width: 768px) { - ul[data-type="taskList"] li > label { - margin-right: 0.5rem; - } -} - -ul[data-type="taskList"] li > label input[type="checkbox"] { - -webkit-appearance: none; - appearance: none; - background-color: rgb(var(--color-background-100)); - margin: 0; - cursor: pointer; - width: 0.8rem; - height: 0.8rem; - position: relative; - border: 1.5px solid rgb(var(--color-text-100)); - margin-right: 0.2rem; - margin-top: 0.15rem; - display: grid; - place-content: center; - - &:hover { - background-color: rgb(var(--color-background-80)); - } - - &:active { - background-color: rgb(var(--color-background-90)); - } - - &::before { - content: ""; - width: 0.5em; - height: 0.5em; - transform: scale(0); - transition: 120ms transform ease-in-out; - box-shadow: inset 1em 1em; - transform-origin: center; - clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%); - } - - &:checked::before { - transform: scale(1); - } -} - -ul[data-type="taskList"] li[data-checked="true"] > div > p { - color: rgb(var(--color-text-200)); - text-decoration: line-through; - text-decoration-thickness: 2px; -} - -/* Overwrite tippy-box original max-width */ - -.tippy-box { - max-width: 400px !important; -} - -.ProseMirror { - position: relative; - word-wrap: break-word; - white-space: pre-wrap; - -moz-tab-size: 4; - tab-size: 4; - -webkit-user-select: text; - -moz-user-select: text; - -ms-user-select: text; - user-select: text; - outline: none; - cursor: text; - line-height: 1.2; - font-family: inherit; - font-size: 14px; - color: inherit; - -moz-box-sizing: border-box; - box-sizing: border-box; - appearance: textfield; - -webkit-appearance: textfield; - -moz-appearance: textfield; -} - -.fadeIn { - opacity: 1; - transition: opacity 0.3s ease-in; -} - -.fadeOut { - opacity: 0; - transition: opacity 0.2s ease-out; -} - -.img-placeholder { - position: relative; - width: 35%; - - &:before { - content: ""; - box-sizing: border-box; - position: absolute; - top: 50%; - left: 45%; - width: 20px; - height: 20px; - border-radius: 50%; - border: 3px solid rgba(var(--color-text-200)); - border-top-color: rgba(var(--color-text-800)); - animation: spinning 0.6s linear infinite; - } -} - -@keyframes spinning { - to { - transform: rotate(360deg); - } -} - -#editor-container { - table { - border-collapse: collapse; - table-layout: fixed; - margin: 0; - border: 1px solid rgb(var(--color-border-200)); - width: 100%; - - td, - th { - min-width: 1em; - border: 1px solid rgb(var(--color-border-200)); - padding: 10px 15px; - vertical-align: top; - box-sizing: border-box; - position: relative; - transition: background-color 0.3s ease; - - > * { - margin-bottom: 0; - } - } - - th { - font-weight: bold; - text-align: left; - background-color: rgb(var(--color-primary-100)); - } - - td:hover { - background-color: rgba(var(--color-primary-300), 0.1); - } - - .selectedCell:after { - z-index: 2; - position: absolute; - content: ""; - left: 0; - right: 0; - top: 0; - bottom: 0; - background-color: rgba(var(--color-primary-300), 0.1); - pointer-events: none; - } - - .column-resize-handle { - position: absolute; - right: -2px; - top: 0; - bottom: -2px; - width: 2px; - background-color: rgb(var(--color-primary-400)); - pointer-events: none; - } - } -} - -.tableWrapper { - overflow-x: auto; -} - -.resize-cursor { - cursor: ew-resize; - cursor: col-resize; -} - -.ProseMirror table * p { - padding: 0px 1px; - margin: 6px 2px; -} - -.ProseMirror table * .is-empty::before { - opacity: 0; -} - -.ProseMirror pre { - background: rgba(var(--color-background-80)); - border-radius: 0.5rem; - color: rgba(var(--color-text-100)); - font-family: "JetBrainsMono", monospace; - padding: 0.75rem 1rem; -} - -.ProseMirror pre code { - background: none; - color: inherit; - font-size: 0.8rem; - padding: 0; -} - -/* .ProseMirror:not(.dragging) .ProseMirror-selectednode:not(.node-image):not(pre) { */ -/* outline: none !important; */ -/* border-radius: 0.2rem; */ -/* background-color: rgb(var(--color-background-90)); */ -/* border: 1px solid #5abbf7; */ -/* padding: 4px 2px 4px 2px; */ -/* transition: background-color 0.2s; */ -/* box-shadow: none; */ -/* } */ - -.drag-handle { - position: fixed; - opacity: 1; - transition: opacity ease-in 0.2s; - height: 18px; - width: 15px; - display: grid; - place-items: center; - z-index: 10; - cursor: grab; - border-radius: 2px; - background-color: rgb(var(--color-background-90)); -} - -.drag-handle:hover { - background-color: rgb(var(--color-background-80)); - transition: background-color 0.2s; -} - -.drag-handle.hidden { - opacity: 0; - pointer-events: none; -} - -@media screen and (max-width: 600px) { - .drag-handle { - display: none; - pointer-events: none; - } -} - -.drag-handle-container { - height: 15px; - width: 15px; - cursor: grab; - display: grid; - place-items: center; -} - -.drag-handle-dots { - height: 100%; - width: 12px; - display: grid; - grid-template-columns: repeat(2, 1fr); - place-items: center; -} - -.drag-handle-dot { - height: 2.75px; - width: 3px; - background-color: rgba(var(--color-text-200)); - border-radius: 50%; -} - -div[data-type="horizontalRule"] { - line-height: 0; - padding: 0.25rem 0; - margin-top: 0; - margin-bottom: 0; - - & > div { - border-bottom: 1px solid rgb(var(--color-text-100)); - } -} - -/* image resizer */ -.moveable-control-box { - z-index: 10 !important; -} diff --git a/web/styles/table.css b/web/styles/table.css deleted file mode 100644 index bce7e4683..000000000 --- a/web/styles/table.css +++ /dev/null @@ -1,206 +0,0 @@ -.tableWrapper { - overflow-x: auto; - padding: 2px; - width: fit-content; - max-width: 100%; -} - -.tableWrapper table { - border-collapse: collapse; - table-layout: fixed; - margin: 0; - margin-bottom: 3rem; - border: 1px solid rgba(var(--color-border-200)); - width: 100%; -} - -.tableWrapper table td, -.tableWrapper table th { - min-width: 1em; - border: 1px solid rgba(var(--color-border-200)); - padding: 10px 15px; - vertical-align: top; - box-sizing: border-box; - position: relative; - transition: background-color 0.3s ease; - - > * { - margin-bottom: 0; - } -} - -.tableWrapper table td > *, -.tableWrapper table th > * { - margin: 0 !important; - padding: 0.25rem 0 !important; -} - -.tableWrapper table td.has-focus, -.tableWrapper table th.has-focus { - box-shadow: rgba(var(--color-primary-300), 0.1) 0px 0px 0px 2px inset !important; -} - -.tableWrapper table th { - font-weight: bold; - text-align: left; - background-color: rgba(var(--color-primary-100)); -} - -.tableWrapper table th * { - font-weight: 600; -} - -.tableWrapper table .selectedCell:after { - z-index: 2; - position: absolute; - content: ""; - left: 0; - right: 0; - top: 0; - bottom: 0; - background-color: rgba(var(--color-primary-300), 0.1); - pointer-events: none; -} - -.tableWrapper table .column-resize-handle { - position: absolute; - right: -2px; - top: 0; - bottom: -2px; - width: 4px; - z-index: 99; - background-color: rgba(var(--color-primary-400)); - pointer-events: none; -} - -.tableWrapper .tableControls { - position: absolute; -} - -.tableWrapper .tableControls .columnsControl, -.tableWrapper .tableControls .rowsControl { - transition: opacity ease-in 100ms; - position: absolute; - z-index: 99; - display: flex; - justify-content: center; - align-items: center; -} - -.tableWrapper .tableControls .columnsControl { - height: 20px; - transform: translateY(-50%); -} - -.tableWrapper .tableControls .columnsControl .columnsControlDiv { - color: white; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' d='M0 0h24v24H0z'/%3E%3Cpath fill='%238F95B2' d='M4.5 10.5c-.825 0-1.5.675-1.5 1.5s.675 1.5 1.5 1.5S6 12.825 6 12s-.675-1.5-1.5-1.5zm15 0c-.825 0-1.5.675-1.5 1.5s.675 1.5 1.5 1.5S21 12.825 21 12s-.675-1.5-1.5-1.5zm-7.5 0c-.825 0-1.5.675-1.5 1.5s.675 1.5 1.5 1.5 1.5-.675 1.5-1.5-.675-1.5-1.5-1.5z'/%3E%3C/svg%3E"); - width: 30px; - height: 15px; -} - -.tableWrapper .tableControls .rowsControl { - width: 20px; - transform: translateX(-50%); -} - -.tableWrapper .tableControls .rowsControl .rowsControlDiv { - color: white; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' d='M0 0h24v24H0z'/%3E%3Cpath fill='%238F95B2' d='M12 3c-.825 0-1.5.675-1.5 1.5S11.175 6 12 6s1.5-.675 1.5-1.5S12.825 3 12 3zm0 15c-.825 0-1.5.675-1.5 1.5S11.175 21 12 21s1.5-.675 1.5-1.5S12.825 18 12 18zm0-7.5c-.825 0-1.5.675-1.5 1.5s.675 1.5 1.5 1.5 1.5-.675 1.5-1.5-.675-1.5-1.5-1.5z'/%3E%3C/svg%3E"); - height: 30px; - width: 15px; -} - -.tableWrapper .tableControls .rowsControlDiv { - background-color: rgba(var(--color-primary-100)); - border: 1px solid rgba(var(--color-border-200)); - border-radius: 2px; - background-size: 1.25rem; - background-repeat: no-repeat; - background-position: center; - transition: transform ease-out 100ms, background-color ease-out 100ms; - outline: none; - box-shadow: #000 0px 2px 4px; - cursor: pointer; -} - -.tableWrapper .tableControls .columnsControlDiv { - background-color: rgba(var(--color-primary-100)); - border: 1px solid rgba(var(--color-border-200)); - border-radius: 2px; - background-size: 1.25rem; - background-repeat: no-repeat; - background-position: center; - transition: transform ease-out 100ms, background-color ease-out 100ms; - outline: none; - box-shadow: #000 0px 2px 4px; - cursor: pointer; -} -.tableWrapper .tableControls .tableToolbox, -.tableWrapper .tableControls .tableColorPickerToolbox { - border: 1px solid rgba(var(--color-border-300)); - background-color: rgba(var(--color-background-100)); - padding: 0.25rem; - display: flex; - flex-direction: column; - width: 200px; - gap: 0.25rem; -} - -.tableWrapper .tableControls .tableToolbox .toolboxItem, -.tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem { - background-color: rgba(var(--color-background-100)); - display: flex; - align-items: center; - gap: 0.5rem; - border: none; - padding: 0.1rem; - border-radius: 4px; - cursor: pointer; - transition: all 0.2s; -} - -.tableWrapper .tableControls .tableToolbox .toolboxItem:hover, -.tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem:hover { - background-color: rgba(var(--color-background-100), 0.5); -} - -.tableWrapper .tableControls .tableToolbox .toolboxItem .iconContainer, -.tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem .iconContainer, -.tableWrapper .tableControls .tableToolbox .toolboxItem .colorContainer, -.tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem .colorContainer { - border: 1px solid rgba(var(--color-border-300)); - border-radius: 3px; - padding: 4px; - display: flex; - align-items: center; - justify-content: center; - width: 1.75rem; - height: 1.75rem; -} - -.tableWrapper .tableControls .tableToolbox .toolboxItem .iconContainer svg, -.tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem .iconContainer svg, -.tableWrapper .tableControls .tableToolbox .toolboxItem .colorContainer svg, -.tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem .colorContainer svg { - width: 2rem; - height: 2rem; -} - -.tableToolbox { - background-color: rgba(var(--color-background-100)); -} - -.tableWrapper .tableControls .tableToolbox .toolboxItem .label, -.tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem .label { - font-size: 0.85rem; - color: rgba(var(--color-text-300)); -} - -.resize-cursor .tableWrapper .tableControls .rowsControl, -.tableWrapper.controls--disabled .tableControls .rowsControl, -.resize-cursor .tableWrapper .tableControls .columnsControl, -.tableWrapper.controls--disabled .tableControls .columnsControl { - opacity: 0; - pointer-events: none; -} diff --git a/web/types/cycles.d.ts b/web/types/cycles.d.ts index c3c5248aa..4f243deeb 100644 --- a/web/types/cycles.d.ts +++ b/web/types/cycles.d.ts @@ -2,6 +2,8 @@ import type { IUser, IIssue, IProjectLite, IWorkspaceLite, IIssueFilterOptions, export type TCycleView = "all" | "active" | "upcoming" | "completed" | "draft"; +export type TCycleGroups = "current" | "upcoming" | "completed" | "draft"; + export type TCycleLayout = "list" | "board" | "gantt"; export interface ICycle { @@ -24,6 +26,7 @@ export interface ICycle { owned_by: IUser; project: string; project_detail: IProjectLite; + status: TCycleGroups; sort_order: number; start_date: string | null; started_issues: number; diff --git a/yarn.lock b/yarn.lock index f27dbe5fe..ad1268469 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2356,7 +2356,7 @@ lodash.merge "^4.6.2" postcss-selector-parser "6.0.10" -"@tiptap/core@^2.1.11", "@tiptap/core@^2.1.12", "@tiptap/core@^2.1.13", "@tiptap/core@^2.1.7": +"@tiptap/core@^2.1.13": version "2.1.13" resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.1.13.tgz#e21f566e81688c826c6f26d2940886734189e193" integrity sha512-cMC8bgTN63dj1Mv82iDeeLl6sa9kY0Pug8LSalxVEptRmyFVsVxGgu2/6Y3T+9aCYScxfS06EkA8SdzFMAwYTQ== @@ -2383,7 +2383,7 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.1.13.tgz#0a26731ebf98ddfd268884ff1712f7189be7b63c" integrity sha512-NkWlQ5bLPUlcROj6G/d4oqAxMf3j3wfndGOPp0z8OoXJtVbVoXl/aMSlLbVgE6n8r6CS8MYxKhXNxrb7Ll2foA== -"@tiptap/extension-code-block-lowlight@^2.1.12": +"@tiptap/extension-code-block-lowlight@^2.1.13": version "2.1.13" resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.1.13.tgz#91110f44d6cc8a12d95ac92aee0c848fdedefb0d" integrity sha512-PlU0lzAEbUGqPykl7fYqlAiY7/zFRtQExsbrpi2kctSIzxC+jgMM4vEpWxLS4jZEXl7jVHvBRH6lRNINDHWmQA== @@ -2398,7 +2398,7 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.1.13.tgz#27a5ca5705e59ca97390fad4d6631bf431690480" integrity sha512-f5fLYlSgliVVa44vd7lQGvo49+peC+Z2H0Fn84TKNCH7tkNZzouoJsHYn0/enLaQ9Sq+24YPfqulfiwlxyiT8w== -"@tiptap/extension-color@^2.1.11": +"@tiptap/extension-color@^2.1.13": version "2.1.13" resolved "https://registry.yarnpkg.com/@tiptap/extension-color/-/extension-color-2.1.13.tgz#f1ea3805db93f308aaf99d8ac80b18fcf13de050" integrity sha512-T3tJXCIfFxzIlGOhvbPVIZa3y36YZRPYIo2TKsgkTz8LiMob6hRXXNFjsrFDp2Fnu3DrBzyvrorsW7767s4eYg== @@ -2445,7 +2445,7 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.1.13.tgz#4884dbf912c8dabbbc69e041ff5529d6337e638e" integrity sha512-7OgjgNqZXvBejgULNdMSma2M1nzv4bbZG+FT5XMFZmEOxR9IB1x/RzChjPdeicff2ZK2sfhMBc4Y9femF5XkUg== -"@tiptap/extension-image@^2.1.7": +"@tiptap/extension-image@^2.1.13": version "2.1.13" resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-2.1.13.tgz#835fc6759b2c1184fb54d3704c538029d523dbf6" integrity sha512-7oVAos+BU4KR/zQsfltrd8hgIxKxyxZ19dhwb1BJI2Nt3Mnx+yFPRlRSehID6RT9dYqgW4UW5d6vh/3HQcYYYw== @@ -2455,19 +2455,19 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.1.13.tgz#1e9521dea002c8d6de833d9fd928d4617623eab8" integrity sha512-HyDJfuDn5hzwGKZiANcvgz6wcum6bEgb4wmJnfej8XanTMJatNVv63TVxCJ10dSc9KGpPVcIkg6W8/joNXIEbw== -"@tiptap/extension-link@^2.1.7": +"@tiptap/extension-link@^2.1.13": version "2.1.13" resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.1.13.tgz#ae4abd7c43292e3a1841488bfc7a687b2f014249" integrity sha512-wuGMf3zRtMHhMrKm9l6Tft5M2N21Z0UP1dZ5t1IlOAvOeYV2QZ5UynwFryxGKLO0NslCBLF/4b/HAdNXbfXWUA== dependencies: linkifyjs "^4.1.0" -"@tiptap/extension-list-item@^2.1.12", "@tiptap/extension-list-item@^2.1.13": +"@tiptap/extension-list-item@^2.1.13": version "2.1.13" resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.1.13.tgz#3c62127df97974f3196866ec00ee397f4c9acdc4" integrity sha512-6e8iiCWXOiJTl1XOwVW2tc0YG18h70HUtEHFCx2m5HspOGFKsFEaSS3qYxOheM9HxlmQeDt8mTtqftRjEFRxPQ== -"@tiptap/extension-mention@^2.1.12": +"@tiptap/extension-mention@^2.1.13": version "2.1.13" resolved "https://registry.yarnpkg.com/@tiptap/extension-mention/-/extension-mention-2.1.13.tgz#6359c563268c46539660958847fe76c22131f2c8" integrity sha512-OYqaucyBiCN/CmDYjpOVX74RJcIEKmAqiZxUi8Gfaq7ryEO5a8Gk93nK+8uZ0onaqHE+mHpoLFFbcAFbOPgkUQ== @@ -2482,7 +2482,7 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.1.13.tgz#30f8ae3f8833c606b339f3554b9ffdbe1e604463" integrity sha512-cEoZBJrsQn69FPpUMePXG/ltGXtqKISgypj70PEHXt5meKDjpmMVSY4/8cXvFYEYsI9GvIwyAK0OrfAHiSoROA== -"@tiptap/extension-placeholder@^2.1.11": +"@tiptap/extension-placeholder@^2.1.13": version "2.1.13" resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.1.13.tgz#b735591f719b9fe89c90dcc6327d2ef2851be510" integrity sha512-vIY7y7UbqsrAW/y8bDE9eRenbQEU16kNHB5Wri8RU1YiUZpkPgdXP/pLqyjIIq95SwP/vdTIHjHoQ77VLRl1hA== @@ -2492,37 +2492,17 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.1.13.tgz#6605792fa98f0e36861be4c7ed4d4125de8c77aa" integrity sha512-VN6zlaCNCbyJUCDyBFxavw19XmQ4LkCh8n20M8huNqW77lDGXA2A7UcWLHaNBpqAijBRu9mWI8l4Bftyf2fcAw== -"@tiptap/extension-table-cell@^2.1.6": - version "2.1.13" - resolved "https://registry.yarnpkg.com/@tiptap/extension-table-cell/-/extension-table-cell-2.1.13.tgz#28efbc99480d53346200dcbf50cfb32bade180d1" - integrity sha512-30pyVt2PxGAk8jmsXKxDheql8K/xIRA9FiDo++kS2Kr6Y7I42/kNPQttJ2W+Q1JdRJvedNfQtziQfKWDRLLCNA== - -"@tiptap/extension-table-header@^2.1.6": - version "2.1.13" - resolved "https://registry.yarnpkg.com/@tiptap/extension-table-header/-/extension-table-header-2.1.13.tgz#8d64a0e5a6a5ea128708b866e56a0e04e34d7a5b" - integrity sha512-FwIV5iso5kmpu01QyvrPCjJqZfqxRTjtjMsDyut2uIgx9v5TXk0V5XvMWobx435ANIDJoGTYCMRlIqcgtyqwAQ== - -"@tiptap/extension-table-row@^2.1.6": - version "2.1.13" - resolved "https://registry.yarnpkg.com/@tiptap/extension-table-row/-/extension-table-row-2.1.13.tgz#ef75d6de9c7695bbb90f745aabd72d327f161ac3" - integrity sha512-27Mb9/oYbiLd+/BUFMhQzRIqMd2Z5j1BZMYsktwtDG8vGdYVlaW257UVaoNR9TmiXyIzd3Dh1mOil8G35+HRHg== - -"@tiptap/extension-table@^2.1.6": - version "2.1.13" - resolved "https://registry.yarnpkg.com/@tiptap/extension-table/-/extension-table-2.1.13.tgz#cfe3fc2665d12d2c946fc83b2cce9d1485ff29a0" - integrity sha512-yMWt2LqotOsWJhLwFNo8fyTwJNLPtnk+eCUxKLlMXP23mJ/lpF+jvTihhHVVic5GqV9vLYZFU2Tn+5k/Vd5P1w== - -"@tiptap/extension-task-item@^2.1.7": +"@tiptap/extension-task-item@^2.1.13": version "2.1.13" resolved "https://registry.yarnpkg.com/@tiptap/extension-task-item/-/extension-task-item-2.1.13.tgz#f049b774f8151b9568d7afbbb5b8fbcb30f35755" integrity sha512-0E1woY0BXpv0SBOGPl5Cmo2RuH+Zchn7dYcTILtOsqHu6onJ4eP0N76+jGFLGV3T0PnPf7JDuwsO/B6pC7yMSg== -"@tiptap/extension-task-list@^2.1.7": +"@tiptap/extension-task-list@^2.1.13": version "2.1.13" resolved "https://registry.yarnpkg.com/@tiptap/extension-task-list/-/extension-task-list-2.1.13.tgz#1a1c40f5886111a1a74386637e7bd0c4ca4158d9" integrity sha512-WfTo4KN0PqpmZxx23rak08M7flfBhv9IcPVpuJ4JthZOlexYdOZxaE/Yd4vlqZhq6cibG7CFljp8VzkfTUa1Ew== -"@tiptap/extension-text-style@^2.1.11": +"@tiptap/extension-text-style@^2.1.13": version "2.1.13" resolved "https://registry.yarnpkg.com/@tiptap/extension-text-style/-/extension-text-style-2.1.13.tgz#4ee7c8cc50272f0977a8d2edae22f063f63d1fe5" integrity sha512-K9/pNHxpZKQoc++crxrsppVUSeHv8YevfY2FkJ4YMaekGcX+q4BRrHR0tOfii4izAUPJF2L0/PexLQaWXtAY1w== @@ -2532,12 +2512,12 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.1.13.tgz#ac17a0220aef1bae1bbd646a91491353e57bb5d1" integrity sha512-zzsTTvu5U67a8WjImi6DrmpX2Q/onLSaj+LRWPh36A1Pz2WaxW5asZgaS+xWCnR+UrozlCALWa01r7uv69jq0w== -"@tiptap/extension-underline@^2.1.7": +"@tiptap/extension-underline@^2.1.13": version "2.1.13" resolved "https://registry.yarnpkg.com/@tiptap/extension-underline/-/extension-underline-2.1.13.tgz#170b4e8e3f03b9defbb7de7cafe4b0a066cea679" integrity sha512-z0CNKPjcvU8TrUSTui1voM7owssyXE9WvEGhIZMHzWwlx2ZXY2/L5+Hh33X/LzSKB9OGf/g1HAuHxrPcYxFuAQ== -"@tiptap/pm@^2.1.12", "@tiptap/pm@^2.1.7": +"@tiptap/pm@^2.1.13": version "2.1.13" resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.1.13.tgz#857753691580be760da13629fab2712c52750741" integrity sha512-zNbA7muWsHuVg12GrTgN/j119rLePPq5M8dZgkKxUwdw8VmU3eUyBp1SihPEXJ2U0MGdZhNhFX7Y74g11u66sg== @@ -2561,12 +2541,7 @@ prosemirror-transform "^1.7.0" prosemirror-view "^1.28.2" -"@tiptap/prosemirror-tables@^1.1.4": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@tiptap/prosemirror-tables/-/prosemirror-tables-1.1.4.tgz#e123978f13c9b5f980066ba660ec5df857755916" - integrity sha512-O2XnDhZV7xTHSFxMMl8Ei3UVeCxuMlbGYZ+J2QG8CzkK8mxDpBa66kFr5DdyAhvdi1ptpcH9u7/GMwItQpN4sA== - -"@tiptap/react@^2.1.7": +"@tiptap/react@^2.1.13": version "2.1.13" resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.1.13.tgz#ec5ebaf9babc145f3329030358b3c5e36016b908" integrity sha512-Dq3f8EtJnpImP3iDtJo+7bulnN9SJZRZcVVzxHXccLcC2MxtmDdlPGZjP+wxO800nd8toSIOd5734fPNf/YcfA== @@ -2574,7 +2549,7 @@ "@tiptap/extension-bubble-menu" "^2.1.13" "@tiptap/extension-floating-menu" "^2.1.13" -"@tiptap/starter-kit@^2.1.10": +"@tiptap/starter-kit@^2.1.13": version "2.1.13" resolved "https://registry.yarnpkg.com/@tiptap/starter-kit/-/starter-kit-2.1.13.tgz#ac33a8b4767b83e4daff8819f4a5f0239df1654c" integrity sha512-ph/mUR/OwPtPkZ5rNHINxubpABn8fHnvJSdhXFrY/q6SKoaO11NZXgegRaiG4aL7O6Sz4LsZVw6Sm0Ae+GJmrg== @@ -2599,7 +2574,7 @@ "@tiptap/extension-strike" "^2.1.13" "@tiptap/extension-text" "^2.1.13" -"@tiptap/suggestion@^2.0.4", "@tiptap/suggestion@^2.1.12": +"@tiptap/suggestion@^2.0.13", "@tiptap/suggestion@^2.1.13": version "2.1.13" resolved "https://registry.yarnpkg.com/@tiptap/suggestion/-/suggestion-2.1.13.tgz#0a8317260baed764a523a09099c0889a0e5b507e" integrity sha512-Y05TsiXTFAJ5SrfoV+21MAxig5UNbY0AVa03lQlh/yicTRPpIc6hgZzblB0uxDSYoj6+kaHE4MIZvPvhUD8BJQ== @@ -2776,7 +2751,7 @@ date-fns "^2.0.1" react-popper "^2.2.5" -"@types/react-dom@^18.2.14", "@types/react-dom@^18.2.17": +"@types/react-dom@^18.2.17": version "18.2.17" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.17.tgz#375c55fab4ae671bd98448dcfa153268d01d6f64" integrity sha512-rvrT/M7Df5eykWFxn6MYt5Pem/Dbyc1N8Y0S9Mrkw2WFCRiqUgw9P7ul2NpwsXCSM1DVdENzdG9J5SreqfAIWg== @@ -2790,7 +2765,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^18.2.39", "@types/react@^18.2.42": +"@types/react@*", "@types/react@18.2.42", "@types/react@^18.2.42": version "18.2.42" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.42.tgz#6f6b11a904f6d96dda3c2920328a97011a00aba7" integrity sha512-c1zEr96MjakLYus/wPnuWDo1/zErfdU9rNsIGmE+NV71nx88FG9Ttgo5dqorXTu/LImX2f63WBP986gJkMPNbA== @@ -8520,47 +8495,47 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -turbo-darwin-64@1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-1.11.1.tgz#fc9c2e578bd2d270b9942b11750a519a39850c57" - integrity sha512-JmwL8kcfxncDf2SZFioSa6dUvpMq/HbMcurh9mGm6BxWLQoB0d3fP/q3HizgCSbOE4ihScXoQ+c/C2xhl6Ngjg== +turbo-darwin-64@1.11.2: + version "1.11.2" + resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-1.11.2.tgz#fc3d4d74b325a27aef11b6a52a61f07d466846b9" + integrity sha512-toFmRG/adriZY3hOps7nYCfqHAS+Ci6xqgX3fbo82kkLpC6OBzcXnleSwuPqjHVAaRNhVoB83L5njcE9Qwi2og== -turbo-darwin-arm64@1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/turbo-darwin-arm64/-/turbo-darwin-arm64-1.11.1.tgz#77ba7f16106dc6e48acad03ce37ccf41d2851017" - integrity sha512-lIpT7nPkU0xmpkI8VOGQcgoQKmUATRMpRhTDclz6j/Px7Qtxjc+2PitKHKfR3aCnseoRMGkgMzPEJTPUwCpnlQ== +turbo-darwin-arm64@1.11.2: + version "1.11.2" + resolved "https://registry.yarnpkg.com/turbo-darwin-arm64/-/turbo-darwin-arm64-1.11.2.tgz#583a4d0025bc3f953a9eeb7065cb173f481a9965" + integrity sha512-FCsEDZ8BUSFYEOSC3rrARQrj7x2VOrmVcfrMUIhexTxproRh4QyMxLfr6LALk4ymx6jbDCxWa6Szal8ckldFbA== -turbo-linux-64@1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-1.11.1.tgz#40458e4db3c7b56b6347a42d9e4aab43acdcf197" - integrity sha512-mHFSqMkgy3h/M8Ocj2oiOr6CqlCqB6coCPWVIAmraBk+SQywwsszgJ69GWBfm7lwwJvb3B1YN1wkZNe9ZZnBfg== +turbo-linux-64@1.11.2: + version "1.11.2" + resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-1.11.2.tgz#55ef996d856cb397b9fb2855a554ccef1cee9dd7" + integrity sha512-Vzda/o/QyEske5CxLf0wcu7UUS+7zB90GgHZV4tyN+WZtoouTvbwuvZ3V6b5Wgd3OJ/JwWR0CXDK7Sf4VEMr7A== -turbo-linux-arm64@1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-1.11.1.tgz#3eb32d42851dbe7fb9627de29687171af2c20295" - integrity sha512-6ybojTkAkymo1Ig7kU3s2YQUUSRf3l2qatPZgw3v4OmFTSU2feCU1sHjAEqhHwEjV1KciDo1wRl1gjjyby4foQ== +turbo-linux-arm64@1.11.2: + version "1.11.2" + resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-1.11.2.tgz#64d6093c9a2f32f410624564fd10685c847d947e" + integrity sha512-bRLwovQRz0yxDZrM4tQEAYV0fBHEaTzUF0JZ8RG1UmZt/CqtpnUrJpYb1VK8hj1z46z9YehARpYCwQ2K0qU4yw== -turbo-windows-64@1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-1.11.1.tgz#b30bd6bd113fee6e455e96f99cf4cc069ef4964b" - integrity sha512-ytWy6+yEtBfv6nbgCKW6HsolgUFAC1PZGMPzbqRGnCm2eLVWhDuwO1Yk7uq4cvdrpXcXgOMcPEoJZxUCDbeJaQ== +turbo-windows-64@1.11.2: + version "1.11.2" + resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-1.11.2.tgz#f4164be9c42796c86ca3929e27f1992a4310b9ed" + integrity sha512-LgTWqkHAKgyVuLYcEPxZVGPInTjjeCnN5KQMdJ4uQZ+xMDROvMFS2rM93iQl4ieDJgidwHCxxCxaU9u8c3d/Kg== -turbo-windows-arm64@1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/turbo-windows-arm64/-/turbo-windows-arm64-1.11.1.tgz#9e152b30232ada57144004f0ff7939933ae0d57d" - integrity sha512-O04DdJoRavOh/v9/MM5wWCEtOekO4aiLljNZc/fOh853sOhid61ZRSEYUmS9ecjmZ/OjKqedIfbkitaQ77c4xA== +turbo-windows-arm64@1.11.2: + version "1.11.2" + resolved "https://registry.yarnpkg.com/turbo-windows-arm64/-/turbo-windows-arm64-1.11.2.tgz#ca1b4d7ac6fe8c931baef1a270ac07bbd924277b" + integrity sha512-829aVBU7IX0c/B4G7g1VI8KniAGutHhIupkYMgF6xPkYVev2G3MYe6DMS/vsLt9GGM9ulDtdWxWrH5P2ngK8IQ== -turbo@^1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/turbo/-/turbo-1.11.1.tgz#1a779ee58382397e2f4c538bc0f15a820081ba8c" - integrity sha512-pmIsyTcyBJ5iJIaTjJyCxAq7YquDqyRai6FW2q0mFAkwK3k0p36wJ5yH85U2Ue6esrTKzeSEKskP4/fa7dv4+A== +turbo@^1.11.2: + version "1.11.2" + resolved "https://registry.yarnpkg.com/turbo/-/turbo-1.11.2.tgz#7bae6df12c210e9b12973aad8f0e7b077039d4ce" + integrity sha512-jPC7LVQJzebs5gWf8FmEvsvXGNyKbN+O9qpvv98xpNaM59aS0/Irhd0H0KbcqnXfsz7ETlzOC3R+xFWthC4Z8A== optionalDependencies: - turbo-darwin-64 "1.11.1" - turbo-darwin-arm64 "1.11.1" - turbo-linux-64 "1.11.1" - turbo-linux-arm64 "1.11.1" - turbo-windows-64 "1.11.1" - turbo-windows-arm64 "1.11.1" + turbo-darwin-64 "1.11.2" + turbo-darwin-arm64 "1.11.2" + turbo-linux-64 "1.11.2" + turbo-linux-arm64 "1.11.2" + turbo-windows-64 "1.11.2" + turbo-windows-arm64 "1.11.2" type-check@^0.4.0, type-check@~0.4.0: version "0.4.0"