diff --git a/.github/workflows/build-branch.yml b/.github/workflows/build-branch.yml new file mode 100644 index 000000000..26b8addd2 --- /dev/null +++ b/.github/workflows/build-branch.yml @@ -0,0 +1,205 @@ + +name: Docker Branch Build + +on: + workflow_dispatch: + inputs: + logLevel: + description: 'Log level' + required: true + default: 'warning' + tags: + description: 'Dev/QA Builds' + +env: + gh_branch: ${{ github.ref_name }} + img_tag: latest + +jobs: + branch_build_and_push: + name: Build-Push Web/Space/API/Proxy Docker Image + runs-on: ubuntu-20.04 + + steps: + - name: Check out the repo + uses: actions/checkout@v3.3.0 + + - uses: ASzc/change-string-case-action@v2 + id: gh_branch_upper_lower + with: + string: ${{ env.gh_branch }} + + - uses: mad9000/actions-find-and-replace-string@2 + id: gh_branch_replace_slash + with: + source: ${{ steps.gh_branch_upper_lower.outputs.lowercase }} + find: '/' + replace: '-' + + - uses: mad9000/actions-find-and-replace-string@2 + id: gh_branch_replace_dot + with: + source: ${{ steps.gh_branch_replace_slash.outputs.value }} + find: '.' + replace: '' + + - uses: mad9000/actions-find-and-replace-string@2 + id: gh_branch_clean + with: + source: ${{ steps.gh_branch_replace_dot.outputs.value }} + find: '_' + replace: '' + - name: Uploading Proxy Source + uses: actions/upload-artifact@v3 + with: + name: proxy-src-code + path: ./nginx + - name: Uploading Backend Source + uses: actions/upload-artifact@v3 + with: + name: backend-src-code + path: ./apiserver + - name: Uploading Web Source + uses: actions/upload-artifact@v3 + with: + name: web-src-code + path: | + ./ + !./apiserver + !./nginx + !./deploy + !./space + + - name: Uploading Space Source + uses: actions/upload-artifact@v3 + with: + name: space-src-code + path: | + ./ + !./apiserver + !./nginx + !./deploy + !./web + outputs: + gh_branch_name: ${{ steps.gh_branch_clean.outputs.value }} + + branch_build_push_frontend: + runs-on: ubuntu-20.04 + needs: [ branch_build_and_push ] + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2.5.0 + + - name: Login to Docker Hub + uses: docker/login-action@v2.1.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Downloading Web Source Code + uses: actions/download-artifact@v3 + with: + name: web-src-code + + - name: Build and Push Frontend to Docker Container Registry + uses: docker/build-push-action@v4.0.0 + with: + context: . + file: ./web/Dockerfile.web + platforms: linux/amd64 + tags: ${{ secrets.DOCKERHUB_USERNAME }}/plane-frontend-private:${{ needs.branch_build_and_push.outputs.gh_branch_name }} + push: true + env: + DOCKER_BUILDKIT: 1 + DOCKER_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKET_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }} + + branch_build_push_space: + runs-on: ubuntu-20.04 + needs: [ branch_build_and_push ] + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2.5.0 + + - name: Login to Docker Hub + uses: docker/login-action@v2.1.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Downloading Space Source Code + uses: actions/download-artifact@v3 + with: + name: space-src-code + + - name: Build and Push Space to Docker Hub + uses: docker/build-push-action@v4.0.0 + with: + context: . + file: ./space/Dockerfile.space + platforms: linux/amd64 + tags: ${{ secrets.DOCKERHUB_USERNAME }}/plane-space-private:${{ needs.branch_build_and_push.outputs.gh_branch_name }} + push: true + env: + DOCKER_BUILDKIT: 1 + DOCKER_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKET_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }} + + branch_build_push_backend: + runs-on: ubuntu-20.04 + needs: [ branch_build_and_push ] + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2.5.0 + + - name: Login to Docker Hub + uses: docker/login-action@v2.1.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Downloading Backend Source Code + uses: actions/download-artifact@v3 + with: + name: backend-src-code + + - name: Build and Push Backend to Docker Hub + uses: docker/build-push-action@v4.0.0 + with: + context: . + file: ./Dockerfile.api + platforms: linux/amd64 + push: true + tags: ${{ secrets.DOCKERHUB_USERNAME }}/plane-backend-private:${{ needs.branch_build_and_push.outputs.gh_branch_name }} + env: + DOCKER_BUILDKIT: 1 + DOCKER_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKET_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }} + + branch_build_push_proxy: + runs-on: ubuntu-20.04 + needs: [ branch_build_and_push ] + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2.5.0 + + - name: Login to Docker Hub + uses: docker/login-action@v2.1.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Downloading Proxy Source Code + uses: actions/download-artifact@v3 + with: + name: proxy-src-code + + - name: Build and Push Plane-Proxy to Docker Hub + uses: docker/build-push-action@v4.0.0 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64 + tags: ${{ secrets.DOCKERHUB_USERNAME }}/plane-proxy-private:${{ needs.branch_build_and_push.outputs.gh_branch_name }} + push: true + env: + DOCKER_BUILDKIT: 1 + DOCKER_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKET_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7568602d3..dcb8b8671 100644 --- a/.gitignore +++ b/.gitignore @@ -75,7 +75,7 @@ pnpm-lock.yaml pnpm-workspace.yaml .npmrc +.secrets tmp/ - ## packages dist diff --git a/apiserver/plane/api/urls/__init__.py b/apiserver/plane/api/urls/__init__.py index 49c2b772e..e4f3718f5 100644 --- a/apiserver/plane/api/urls/__init__.py +++ b/apiserver/plane/api/urls/__init__.py @@ -1,7 +1,7 @@ from .analytic import urlpatterns as analytic_urls from .asset import urlpatterns as asset_urls from .authentication import urlpatterns as authentication_urls -from .configuration import urlpatterns as configuration_urls +from .config import urlpatterns as configuration_urls from .cycle import urlpatterns as cycle_urls from .estimate import urlpatterns as estimate_urls from .gpt import urlpatterns as gpt_urls diff --git a/apiserver/plane/api/urls/configuration.py b/apiserver/plane/api/urls/config.py similarity index 100% rename from apiserver/plane/api/urls/configuration.py rename to apiserver/plane/api/urls/config.py diff --git a/apiserver/plane/api/views/config.py b/apiserver/plane/api/views/config.py index f59ca04a0..687cb211c 100644 --- a/apiserver/plane/api/views/config.py +++ b/apiserver/plane/api/views/config.py @@ -30,4 +30,5 @@ class ConfigurationEndpoint(BaseAPIView): data["email_password_login"] = ( os.environ.get("ENABLE_EMAIL_PASSWORD", "0") == "1" ) + data["slack"] = os.environ.get("SLACK_CLIENT_ID", None) return Response(data, status=status.HTTP_200_OK) diff --git a/apiserver/plane/api/views/integration/base.py b/apiserver/plane/api/views/integration/base.py index 65b94d0a1..cc911b537 100644 --- a/apiserver/plane/api/views/integration/base.py +++ b/apiserver/plane/api/views/integration/base.py @@ -1,6 +1,6 @@ # Python improts import uuid - +import requests # Django imports from django.contrib.auth.hashers import make_password @@ -25,7 +25,7 @@ from plane.utils.integrations.github import ( delete_github_installation, ) from plane.api.permissions import WorkSpaceAdminPermission - +from plane.utils.integrations.slack import slack_oauth class IntegrationViewSet(BaseViewSet): serializer_class = IntegrationSerializer @@ -98,12 +98,19 @@ class WorkspaceIntegrationViewSet(BaseViewSet): config = {"installation_id": installation_id} if provider == "slack": - metadata = request.data.get("metadata", {}) + code = request.data.get("code", False) + + if not code: + return Response({"error": "Code is required"}, status=status.HTTP_400_BAD_REQUEST) + + slack_response = slack_oauth(code=code) + + metadata = slack_response access_token = metadata.get("access_token", False) team_id = metadata.get("team", {}).get("id", False) if not metadata or not access_token or not team_id: return Response( - {"error": "Access token and team id is required"}, + {"error": "Slack could not be installed. Please try again later"}, status=status.HTTP_400_BAD_REQUEST, ) config = {"team_id": team_id, "access_token": access_token} diff --git a/apiserver/plane/api/views/integration/slack.py b/apiserver/plane/api/views/integration/slack.py index 83aa951ba..863b6ba0c 100644 --- a/apiserver/plane/api/views/integration/slack.py +++ b/apiserver/plane/api/views/integration/slack.py @@ -11,6 +11,7 @@ from plane.api.views import BaseViewSet, BaseAPIView from plane.db.models import SlackProjectSync, WorkspaceIntegration, ProjectMember from plane.api.serializers import SlackProjectSyncSerializer from plane.api.permissions import ProjectBasePermission, ProjectEntityPermission +from plane.utils.integrations.slack import slack_oauth class SlackProjectSyncViewSet(BaseViewSet): @@ -32,25 +33,46 @@ class SlackProjectSyncViewSet(BaseViewSet): ) def create(self, request, slug, project_id, workspace_integration_id): - serializer = SlackProjectSyncSerializer(data=request.data) + try: + code = request.data.get("code", False) - workspace_integration = WorkspaceIntegration.objects.get( - workspace__slug=slug, pk=workspace_integration_id - ) + if not code: + return Response( + {"error": "Code is required"}, status=status.HTTP_400_BAD_REQUEST + ) - if serializer.is_valid(): - serializer.save( - project_id=project_id, - workspace_integration_id=workspace_integration_id, + slack_response = slack_oauth(code=code) + + workspace_integration = WorkspaceIntegration.objects.get( + workspace__slug=slug, pk=workspace_integration_id ) workspace_integration = WorkspaceIntegration.objects.get( pk=workspace_integration_id, workspace__slug=slug ) - + slack_project_sync = SlackProjectSync.objects.create( + access_token=slack_response.get("access_token"), + scopes=slack_response.get("scope"), + bot_user_id=slack_response.get("bot_user_id"), + webhook_url=slack_response.get("incoming_webhook", {}).get("url"), + data=slack_response, + team_id=slack_response.get("team", {}).get("id"), + team_name=slack_response.get("team", {}).get("name"), + workspace_integration=workspace_integration, + ) _ = ProjectMember.objects.get_or_create( member=workspace_integration.actor, role=20, project_id=project_id ) - + serializer = SlackProjectSyncSerializer(slack_project_sync) return Response(serializer.data, status=status.HTTP_200_OK) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + except IntegrityError as e: + if "already exists" in str(e): + return Response( + {"error": "Slack is already installed for the project"}, + status=status.HTTP_410_GONE, + ) + capture_exception(e) + return Response( + {"error": "Slack could not be installed. Please try again later"}, + status=status.HTTP_400_BAD_REQUEST, + ) diff --git a/apiserver/plane/utils/integrations/slack.py b/apiserver/plane/utils/integrations/slack.py new file mode 100644 index 000000000..70f26e160 --- /dev/null +++ b/apiserver/plane/utils/integrations/slack.py @@ -0,0 +1,20 @@ +import os +import requests + +def slack_oauth(code): + SLACK_OAUTH_URL = os.environ.get("SLACK_OAUTH_URL", False) + SLACK_CLIENT_ID = os.environ.get("SLACK_CLIENT_ID", False) + SLACK_CLIENT_SECRET = os.environ.get("SLACK_CLIENT_SECRET", False) + + # Oauth Slack + if SLACK_OAUTH_URL and SLACK_CLIENT_ID and SLACK_CLIENT_SECRET: + response = requests.get( + SLACK_OAUTH_URL, + params={ + "code": code, + "client_id": SLACK_CLIENT_ID, + "client_secret": SLACK_CLIENT_SECRET, + }, + ) + return response.json() + return {} diff --git a/packages/editor/core/package.json b/packages/editor/core/package.json index 83f59a1f4..ab6c77724 100644 --- a/packages/editor/core/package.json +++ b/packages/editor/core/package.json @@ -2,6 +2,7 @@ "name": "@plane/editor-core", "version": "0.0.1", "description": "Core Editor that powers Plane", + "private": true, "main": "./dist/index.mjs", "module": "./dist/index.mjs", "types": "./dist/index.d.mts", diff --git a/packages/editor/lite-text-editor/package.json b/packages/editor/lite-text-editor/package.json index 47ef154c6..3b6cd720b 100644 --- a/packages/editor/lite-text-editor/package.json +++ b/packages/editor/lite-text-editor/package.json @@ -2,6 +2,7 @@ "name": "@plane/lite-text-editor", "version": "0.0.1", "description": "Package that powers Plane's Comment Editor", + "private": true, "main": "./dist/index.mjs", "module": "./dist/index.mjs", "types": "./dist/index.d.mts", @@ -29,9 +30,6 @@ "dependencies": { "@plane/editor-core": "*", "@tiptap/extension-list-item": "^2.1.11", - "@types/node": "18.15.3", - "@types/react": "^18.2.5", - "@types/react-dom": "18.0.11", "class-variance-authority": "^0.7.0", "clsx": "^1.2.1", "eslint": "8.36.0", @@ -46,6 +44,9 @@ "use-debounce": "^9.0.4" }, "devDependencies": { + "@types/node": "18.15.3", + "@types/react": "^18.2.35", + "@types/react-dom": "^18.2.14", "eslint": "^7.32.0", "postcss": "^8.4.29", "tailwind-config-custom": "*", diff --git a/packages/editor/lite-text-editor/src/ui/index.tsx b/packages/editor/lite-text-editor/src/ui/index.tsx index 6cd03bcfa..ef321d511 100644 --- a/packages/editor/lite-text-editor/src/ui/index.tsx +++ b/packages/editor/lite-text-editor/src/ui/index.tsx @@ -1,4 +1,3 @@ -"use client"; import * as React from "react"; import { EditorContainer, @@ -32,7 +31,7 @@ interface ILiteTextEditor { editorContentCustomClassNames?: string; onChange?: (json: any, html: string) => void; setIsSubmitting?: ( - isSubmitting: "submitting" | "submitted" | "saved", + isSubmitting: "submitting" | "submitted" | "saved" ) => void; setShouldShowAlert?: (showAlert: boolean) => void; forwardedRef?: any; @@ -127,7 +126,7 @@ const LiteTextEditor = (props: LiteTextEditorProps) => { }; const LiteTextEditorWithRef = React.forwardRef( - (props, ref) => , + (props, ref) => ); LiteTextEditorWithRef.displayName = "LiteTextEditorWithRef"; diff --git a/packages/editor/lite-text-editor/src/ui/read-only/index.tsx b/packages/editor/lite-text-editor/src/ui/read-only/index.tsx index 5dccbe977..a3de061ae 100644 --- a/packages/editor/lite-text-editor/src/ui/read-only/index.tsx +++ b/packages/editor/lite-text-editor/src/ui/read-only/index.tsx @@ -1,6 +1,10 @@ -"use client" -import { EditorContainer, EditorContentWrapper, getEditorClassNames, useReadOnlyEditor } from '@plane/editor-core'; -import * as React from 'react'; +import * as React from "react"; +import { + EditorContainer, + EditorContentWrapper, + getEditorClassNames, + useReadOnlyEditor, +} from "@plane/editor-core"; interface ICoreReadOnlyEditor { value: string; @@ -8,7 +12,7 @@ interface ICoreReadOnlyEditor { noBorder?: boolean; borderOnFocus?: boolean; customClassName?: string; - mentionHighlights: string[] + mentionHighlights: string[]; } interface EditorCoreProps extends ICoreReadOnlyEditor { @@ -27,31 +31,39 @@ const LiteReadOnlyEditor = ({ customClassName, value, forwardedRef, - mentionHighlights + mentionHighlights, }: EditorCoreProps) => { const editor = useReadOnlyEditor({ value, forwardedRef, - mentionHighlights + mentionHighlights, }); - const editorClassNames = getEditorClassNames({ noBorder, borderOnFocus, customClassName }); + const editorClassNames = getEditorClassNames({ + noBorder, + borderOnFocus, + customClassName, + }); if (!editor) return null; return (
- +
-
+ ); }; -const LiteReadOnlyEditorWithRef = React.forwardRef((props, ref) => ( - -)); +const LiteReadOnlyEditorWithRef = React.forwardRef< + EditorHandle, + ICoreReadOnlyEditor +>((props, ref) => ); LiteReadOnlyEditorWithRef.displayName = "LiteReadOnlyEditorWithRef"; -export { LiteReadOnlyEditor , LiteReadOnlyEditorWithRef }; +export { LiteReadOnlyEditor, LiteReadOnlyEditorWithRef }; diff --git a/packages/editor/lite-text-editor/src/ui/tooltip.tsx b/packages/editor/lite-text-editor/src/ui/tooltip.tsx index f29d8a491..a2f2414e5 100644 --- a/packages/editor/lite-text-editor/src/ui/tooltip.tsx +++ b/packages/editor/lite-text-editor/src/ui/tooltip.tsx @@ -1,5 +1,4 @@ -import * as React from 'react'; - +import * as React from "react"; // next-themes import { useTheme } from "next-themes"; // tooltip2 @@ -69,8 +68,16 @@ export const Tooltip: React.FC = ({ } position={position} - renderTarget={({ isOpen: isTooltipOpen, ref: eleReference, ...tooltipProps }) => - React.cloneElement(children, { ref: eleReference, ...tooltipProps, ...children.props }) + renderTarget={({ + isOpen: isTooltipOpen, + ref: eleReference, + ...tooltipProps + }) => + React.cloneElement(children, { + ref: eleReference, + ...tooltipProps, + ...children.props, + }) } /> ); diff --git a/packages/editor/rich-text-editor/package.json b/packages/editor/rich-text-editor/package.json index 7bdd0a58b..db793261c 100644 --- a/packages/editor/rich-text-editor/package.json +++ b/packages/editor/rich-text-editor/package.json @@ -2,6 +2,7 @@ "name": "@plane/rich-text-editor", "version": "0.0.1", "description": "Rich Text Editor that powers Plane", + "private": true, "main": "./dist/index.mjs", "module": "./dist/index.mjs", "types": "./dist/index.d.mts", @@ -21,19 +22,19 @@ "check-types": "tsc --noEmit" }, "peerDependencies": { + "@tiptap/core": "^2.1.11", "next": "12.3.2", "next-themes": "^0.2.1", "react": "^18.2.0", - "react-dom": "18.2.0", - "@tiptap/core": "^2.1.11" + "react-dom": "18.2.0" }, "dependencies": { "@plane/editor-core": "*", "@tiptap/extension-code-block-lowlight": "^2.1.11", "@tiptap/extension-horizontal-rule": "^2.1.11", "@tiptap/extension-placeholder": "^2.1.11", - "class-variance-authority": "^0.7.0", "@tiptap/suggestion": "^2.1.7", + "class-variance-authority": "^0.7.0", "clsx": "^1.2.1", "highlight.js": "^11.8.0", "lowlight": "^3.0.0", @@ -41,8 +42,8 @@ }, "devDependencies": { "@types/node": "18.15.3", - "@types/react": "^18.2.5", - "@types/react-dom": "18.0.11", + "@types/react": "^18.2.35", + "@types/react-dom": "^18.2.14", "eslint": "^7.32.0", "postcss": "^8.4.29", "react": "^18.2.0", diff --git a/packages/editor/rich-text-editor/src/ui/read-only/index.tsx b/packages/editor/rich-text-editor/src/ui/read-only/index.tsx index dc058cf89..46905f263 100644 --- a/packages/editor/rich-text-editor/src/ui/read-only/index.tsx +++ b/packages/editor/rich-text-editor/src/ui/read-only/index.tsx @@ -8,6 +8,7 @@ interface IRichTextReadOnlyEditor { noBorder?: boolean; borderOnFocus?: boolean; customClassName?: string; + mentionHighlights?: string[]; } interface RichTextReadOnlyEditorProps extends IRichTextReadOnlyEditor { @@ -26,10 +27,12 @@ const RichReadOnlyEditor = ({ customClassName, value, forwardedRef, + mentionHighlights, }: RichTextReadOnlyEditorProps) => { const editor = useReadOnlyEditor({ value, forwardedRef, + mentionHighlights, }); const editorClassNames = getEditorClassNames({ noBorder, borderOnFocus, customClassName }); diff --git a/packages/eslint-config-custom/package.json b/packages/eslint-config-custom/package.json index 12a7ab8c8..11e970d0e 100644 --- a/packages/eslint-config-custom/package.json +++ b/packages/eslint-config-custom/package.json @@ -1,5 +1,6 @@ { "name": "eslint-config-custom", + "private": true, "version": "0.13.2", "main": "index.js", "license": "MIT", diff --git a/packages/tailwind-config-custom/package.json b/packages/tailwind-config-custom/package.json index 1336379b7..286dfc3b6 100644 --- a/packages/tailwind-config-custom/package.json +++ b/packages/tailwind-config-custom/package.json @@ -3,6 +3,7 @@ "version": "0.13.2", "description": "common tailwind configuration across monorepo", "main": "index.js", + "private": true, "devDependencies": { "@tailwindcss/typography": "^0.5.9", "autoprefixer": "^10.4.14", diff --git a/packages/ui/package.json b/packages/ui/package.json index f76bd8374..72413eb7c 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,5 +1,7 @@ { "name": "@plane/ui", + "description": "UI components shared across multiple apps internally", + "private": true, "version": "0.0.1", "main": "./dist/index.js", "module": "./dist/index.mjs", diff --git a/packages/ui/src/avatar/avatar-group.tsx b/packages/ui/src/avatar/avatar-group.tsx index 4abb4a93b..25a3c76fc 100644 --- a/packages/ui/src/avatar/avatar-group.tsx +++ b/packages/ui/src/avatar/avatar-group.tsx @@ -35,8 +35,11 @@ export const AvatarGroup: React.FC = (props) => { // calculate total length of avatars inside the group const totalAvatars = React.Children.toArray(children).length; + // if avatars are equal to max + 1, then we need to show the last avatar as well, if avatars are more than max + 1, then we need to show the count of the remaining avatars + const maxAvatarsToRender = totalAvatars <= max + 1 ? max + 1 : max; + // slice the children to the maximum number of avatars - const avatars = React.Children.toArray(children).slice(0, max); + const avatars = React.Children.toArray(children).slice(0, maxAvatarsToRender); // assign the necessary props from the AvatarGroup component to the Avatar components const avatarsWithUpdatedProps = avatars.map((avatar) => { @@ -54,11 +57,14 @@ export const AvatarGroup: React.FC = (props) => { return (
{avatarsWithUpdatedProps.map((avatar, index) => ( -
+
{avatar}
))} - {max < totalAvatars && ( + {maxAvatarsToRender < totalAvatars && ( = (props) => {
= ({ - width = "14", - height = "14", - className, - color = issueGroupColors["backlog"], -}) => ( - - - -); diff --git a/space/components/icons/state-group/cancelled-state-icon.tsx b/space/components/icons/state-group/cancelled-state-icon.tsx deleted file mode 100644 index e244c191a..000000000 --- a/space/components/icons/state-group/cancelled-state-icon.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React from "react"; -// types -import type { Props } from "../types"; -// constants -import { issueGroupColors } from "constants/data"; - -export const CancelledStateIcon: React.FC = ({ - width = "14", - height = "14", - className, - color = issueGroupColors["cancelled"], -}) => ( - - - - - - - - - - - - - -); diff --git a/space/components/icons/state-group/completed-state-icon.tsx b/space/components/icons/state-group/completed-state-icon.tsx deleted file mode 100644 index 417ebbf3f..000000000 --- a/space/components/icons/state-group/completed-state-icon.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React from "react"; -// types -import type { Props } from "../types"; -// constants -import { issueGroupColors } from "constants/data"; - -export const CompletedStateIcon: React.FC = ({ - width = "14", - height = "14", - className, - color = issueGroupColors["completed"], -}) => ( - - - - - - - - - - - - -); diff --git a/space/components/icons/state-group/index.ts b/space/components/icons/state-group/index.ts deleted file mode 100644 index 6ede38df6..000000000 --- a/space/components/icons/state-group/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from "./backlog-state-icon"; -export * from "./cancelled-state-icon"; -export * from "./completed-state-icon"; -export * from "./started-state-icon"; -export * from "./state-group-icon"; -export * from "./unstarted-state-icon"; diff --git a/space/components/icons/state-group/started-state-icon.tsx b/space/components/icons/state-group/started-state-icon.tsx deleted file mode 100644 index 4ebd1771f..000000000 --- a/space/components/icons/state-group/started-state-icon.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React from "react"; -// types -import type { Props } from "../types"; -// constants -import { issueGroupColors } from "constants/data"; - -export const StartedStateIcon: React.FC = ({ - width = "14", - height = "14", - className, - color = issueGroupColors["started"], -}) => ( - - - - - - - - - - - - -); diff --git a/space/components/icons/state-group/state-group-icon.tsx b/space/components/icons/state-group/state-group-icon.tsx deleted file mode 100644 index 1af523400..000000000 --- a/space/components/icons/state-group/state-group-icon.tsx +++ /dev/null @@ -1,29 +0,0 @@ -// icons -import { - BacklogStateIcon, - CancelledStateIcon, - CompletedStateIcon, - StartedStateIcon, - UnstartedStateIcon, -} from "components/icons"; -import { TIssueGroupKey } from "types/issue"; - -type Props = { - stateGroup: TIssueGroupKey; - color: string; - className?: string; - height?: string; - width?: string; -}; - -export const StateGroupIcon: React.FC = ({ stateGroup, className, color, height = "12px", width = "12px" }) => { - if (stateGroup === "backlog") - return ; - else if (stateGroup === "cancelled") - return ; - else if (stateGroup === "completed") - return ; - else if (stateGroup === "started") - return ; - else return ; -}; diff --git a/space/components/icons/state-group/unstarted-state-icon.tsx b/space/components/icons/state-group/unstarted-state-icon.tsx deleted file mode 100644 index f79bc00fc..000000000 --- a/space/components/icons/state-group/unstarted-state-icon.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React from "react"; -// types -import type { Props } from "../types"; -// constants -import { issueGroupColors } from "constants/data"; - -export const UnstartedStateIcon: React.FC = ({ - width = "14", - height = "14", - className, - color = issueGroupColors["unstarted"], -}) => ( - - - - - - - - - - -); diff --git a/space/components/icons/types.d.ts b/space/components/icons/types.d.ts deleted file mode 100644 index f82a18147..000000000 --- a/space/components/icons/types.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type Props = { - width?: string | number; - height?: string | number; - color?: string; - className?: string; -}; diff --git a/space/components/issues/board-views/block-state.tsx b/space/components/issues/board-views/block-state.tsx index 16792c81b..2daba1226 100644 --- a/space/components/issues/board-views/block-state.tsx +++ b/space/components/issues/board-views/block-state.tsx @@ -1,3 +1,5 @@ +// ui +import { StateGroupIcon } from "@plane/ui"; // constants import { issueGroupFilter } from "constants/data"; @@ -8,7 +10,7 @@ export const IssueBlockState = ({ state }: any) => { return (
- +
{state?.name}
diff --git a/space/components/issues/board-views/kanban/block.tsx b/space/components/issues/board-views/kanban/block.tsx index b4de76f2b..b2effc4ad 100644 --- a/space/components/issues/board-views/kanban/block.tsx +++ b/space/components/issues/board-views/kanban/block.tsx @@ -7,7 +7,6 @@ import { useMobxStore } from "lib/mobx/store-provider"; // components import { IssueBlockPriority } from "components/issues/board-views/block-priority"; import { IssueBlockState } from "components/issues/board-views/block-state"; -import { IssueBlockLabels } from "components/issues/board-views/block-labels"; import { IssueBlockDueDate } from "components/issues/board-views/block-due-date"; // interfaces import { IIssue } from "types/issue"; @@ -37,7 +36,7 @@ export const IssueListBlock = observer(({ issue }: { issue: IIssue }) => { }; return ( -
+
{/* id */}
{projectStore?.project?.identifier}-{issue?.sequence_id} diff --git a/space/components/issues/board-views/kanban/header.tsx b/space/components/issues/board-views/kanban/header.tsx index 5645e2b3b..8f2f28496 100644 --- a/space/components/issues/board-views/kanban/header.tsx +++ b/space/components/issues/board-views/kanban/header.tsx @@ -4,8 +4,8 @@ import { observer } from "mobx-react-lite"; import { IIssueState } from "types/issue"; // constants import { issueGroupFilter } from "constants/data"; -// icons -import { StateGroupIcon } from "components/icons"; +// ui +import { StateGroupIcon } from "@plane/ui"; // mobx hook import { useMobxStore } from "lib/mobx/store-provider"; import { RootStore } from "store/root"; @@ -18,11 +18,11 @@ export const IssueListHeader = observer(({ state }: { state: IIssueState }) => { if (stateGroup === null) return <>; return ( -
-
- +
+
+
-
{state?.name}
+
{state?.name}
{store.issue.getCountOfIssuesByState(state.id)} diff --git a/space/components/issues/board-views/list/block.tsx b/space/components/issues/board-views/list/block.tsx index bdf39b84f..57011d033 100644 --- a/space/components/issues/board-views/list/block.tsx +++ b/space/components/issues/board-views/list/block.tsx @@ -38,10 +38,10 @@ export const IssueListBlock: FC<{ issue: IIssue }> = observer((props) => { }; return ( -
-
+
+
{/* id */} -
+
{projectStore?.project?.identifier}-{issue?.sequence_id}
{/* name */} diff --git a/space/components/issues/board-views/list/header.tsx b/space/components/issues/board-views/list/header.tsx index 83312e7b9..fc7e5ef61 100644 --- a/space/components/issues/board-views/list/header.tsx +++ b/space/components/issues/board-views/list/header.tsx @@ -2,8 +2,8 @@ import { observer } from "mobx-react-lite"; // interfaces import { IIssueState } from "types/issue"; -// icons -import { StateGroupIcon } from "components/icons"; +// ui +import { StateGroupIcon } from "@plane/ui"; // constants import { issueGroupFilter } from "constants/data"; // mobx hook @@ -18,12 +18,12 @@ export const IssueListHeader = observer(({ state }: { state: IIssueState }) => { if (stateGroup === null) return <>; return ( -
-
- +
+
+
-
{state?.name}
-
{store.issue.getCountOfIssuesByState(state.id)}
+
{state?.name}
+
{store.issue.getCountOfIssuesByState(state.id)}
); }); diff --git a/space/components/issues/board-views/list/index.tsx b/space/components/issues/board-views/list/index.tsx index 1c6900dd9..d6b11d026 100644 --- a/space/components/issues/board-views/list/index.tsx +++ b/space/components/issues/board-views/list/index.tsx @@ -27,9 +27,7 @@ export const IssueListView = observer(() => { ))}
) : ( -
- No Issues are available. -
+
No issues.
)}
))} diff --git a/space/components/issues/peek-overview/comment/add-comment.tsx b/space/components/issues/peek-overview/comment/add-comment.tsx index c7e7a468e..f70a2c5aa 100644 --- a/space/components/issues/peek-overview/comment/add-comment.tsx +++ b/space/components/issues/peek-overview/comment/add-comment.tsx @@ -7,7 +7,7 @@ import { useMobxStore } from "lib/mobx/store-provider"; // hooks import useToast from "hooks/use-toast"; // ui -import { SecondaryButton } from "components/ui"; +import { Button } from "@plane/ui"; // types import { Comment } from "types/issue"; // components @@ -29,7 +29,6 @@ export const AddComment: React.FC = observer((props) => { const { handleSubmit, control, - setValue, watch, formState: { isSubmitting }, reset, @@ -85,27 +84,30 @@ export const AddComment: React.FC = observer((props) => { ? watch("comment_html") : value } - customClassName="p-3 min-h-[50px] shadow-sm" + customClassName="p-2" + editorContentCustomClassNames="min-h-[35px]" debouncedUpdatesEnabled={false} onChange={(comment_json: Object, comment_html: string) => { onChange(comment_html); }} + submitButton={ + + } /> )} /> - - { - userStore.requiredLogin(() => { - handleSubmit(onSubmit)(e); - }); - }} - type="submit" - disabled={isSubmitting || disabled} - className="mt-2" - > - {isSubmitting ? "Adding..." : "Comment"} -
); diff --git a/space/components/issues/peek-overview/comment/comment-detail-card.tsx b/space/components/issues/peek-overview/comment/comment-detail-card.tsx index b4754f098..29801c9e6 100644 --- a/space/components/issues/peek-overview/comment/comment-detail-card.tsx +++ b/space/components/issues/peek-overview/comment/comment-detail-card.tsx @@ -15,6 +15,7 @@ import { timeAgo } from "helpers/date-time.helper"; import { Comment } from "types/issue"; // services import fileService from "services/file.service"; +import useEditorSuggestions from "hooks/use-editor-suggestions"; type Props = { workspaceSlug: string; @@ -28,6 +29,8 @@ export const CommentCard: React.FC = observer((props) => { // states const [isEditing, setIsEditing] = useState(false); + const mentionsConfig = useEditorSuggestions(); + const editorRef = React.useRef(null); const showEditorRef = React.useRef(null); @@ -135,6 +138,7 @@ export const CommentCard: React.FC = observer((props) => { ref={showEditorRef} value={comment.comment_html} customClassName="text-xs border border-custom-border-200 bg-custom-background-100" + mentionHighlights={mentionsConfig.mentionHighlights} />
diff --git a/space/components/issues/peek-overview/issue-details.tsx b/space/components/issues/peek-overview/issue-details.tsx index 24dd65651..9b8634416 100644 --- a/space/components/issues/peek-overview/issue-details.tsx +++ b/space/components/issues/peek-overview/issue-details.tsx @@ -2,27 +2,33 @@ import { IssueReactions } from "components/issues/peek-overview"; import { RichReadOnlyEditor } from "@plane/rich-text-editor"; // types import { IIssue } from "types/issue"; +import useEditorSuggestions from "hooks/use-editor-suggestions"; type Props = { issueDetails: IIssue; }; -export const PeekOverviewIssueDetails: React.FC = ({ issueDetails }) => ( -
-
- {issueDetails.project_detail.identifier}-{issueDetails.sequence_id} -
-

{issueDetails.name}

- {issueDetails.description_html !== "" && issueDetails.description_html !== "

" && ( -

" - : issueDetails.description_html} - customClassName="p-3 min-h-[50px] shadow-sm" /> - )} - -
-); +export const PeekOverviewIssueDetails: React.FC = ({ issueDetails }) => { + + const mentionConfig = useEditorSuggestions(); + + return ( +
+
+ {issueDetails.project_detail.identifier}-{issueDetails.sequence_id} +
+

{issueDetails.name}

+ {issueDetails.description_html !== "" && issueDetails.description_html !== "

" && ( +

" + : issueDetails.description_html} + customClassName="p-3 min-h-[50px] shadow-sm" mentionHighlights={mentionConfig.mentionHighlights} /> + )} + +
+ ) +}; diff --git a/space/hooks/use-editor-suggestions.tsx b/space/hooks/use-editor-suggestions.tsx new file mode 100644 index 000000000..0659121b7 --- /dev/null +++ b/space/hooks/use-editor-suggestions.tsx @@ -0,0 +1,13 @@ +import { useMobxStore } from "lib/mobx/store-provider"; +import { RootStore } from "store/root"; + +const useEditorSuggestions = () => { + const { mentionsStore }: RootStore = useMobxStore(); + + return { + // mentionSuggestions: mentionsStore.mentionSuggestions, + mentionHighlights: mentionsStore.mentionHighlights, + }; +}; + +export default useEditorSuggestions; diff --git a/space/store/mentions.store.ts b/space/store/mentions.store.ts new file mode 100644 index 000000000..ca4a1a3c1 --- /dev/null +++ b/space/store/mentions.store.ts @@ -0,0 +1,45 @@ +import { IMentionHighlight } from "@plane/lite-text-editor"; +import { RootStore } from "./root"; +import { computed, makeObservable } from "mobx"; + +export interface IMentionsStore { + // mentionSuggestions: IMentionSuggestion[]; + mentionHighlights: IMentionHighlight[]; +} + +export class MentionsStore implements IMentionsStore{ + + // root store + rootStore; + + constructor(_rootStore: RootStore ){ + + // rootStore + this.rootStore = _rootStore; + + makeObservable(this, { + mentionHighlights: computed, + // mentionSuggestions: computed + }) + } + + // get mentionSuggestions() { + // const projectMembers = this.rootStore.project.project. + + // const suggestions = projectMembers === null ? [] : projectMembers.map((member) => ({ + // id: member.member.id, + // type: "User", + // title: member.member.display_name, + // subtitle: member.member.email ?? "", + // avatar: member.member.avatar, + // redirect_uri: `/${member.workspace.slug}/profile/${member.member.id}`, + // })) + + // return suggestions + // } + + get mentionHighlights() { + const user = this.rootStore.user.currentUser; + return user ? [user.id] : [] + } +} \ No newline at end of file diff --git a/space/store/root.ts b/space/store/root.ts index 6b87020ef..22b951d20 100644 --- a/space/store/root.ts +++ b/space/store/root.ts @@ -5,6 +5,7 @@ import UserStore from "./user"; import IssueStore, { IIssueStore } from "./issue"; import ProjectStore, { IProjectStore } from "./project"; import IssueDetailStore, { IIssueDetailStore } from "./issue_details"; +import { IMentionsStore, MentionsStore } from "./mentions.store"; enableStaticRendering(typeof window === "undefined"); @@ -13,11 +14,13 @@ export class RootStore { issue: IIssueStore; issueDetails: IIssueDetailStore; project: IProjectStore; + mentionsStore: IMentionsStore; constructor() { this.user = new UserStore(this); this.issue = new IssueStore(this); this.project = new ProjectStore(this); this.issueDetails = new IssueDetailStore(this); + this.mentionsStore = new MentionsStore(this); } } diff --git a/space/store/user.ts b/space/store/user.ts index cec2d340f..e2b6428ef 100644 --- a/space/store/user.ts +++ b/space/store/user.ts @@ -2,7 +2,6 @@ import { observable, action, computed, makeObservable, runInAction } from "mobx"; // service import UserService from "services/user.service"; -import { ActorDetail } from "types/issue"; // types import { IUser } from "types/user"; diff --git a/space/styles/globals.css b/space/styles/globals.css index 1782b9b81..ea04bcda6 100644 --- a/space/styles/globals.css +++ b/space/styles/globals.css @@ -199,9 +199,9 @@ --color-sidebar-text-400: var(--color-text-400); /* sidebar placeholder text */ --color-sidebar-border-100: var(--color-border-100); /* subtle sidebar border= 1 */ - --color-sidebar-border-200: var(--color-border-100); /* subtle sidebar border- 2 */ - --color-sidebar-border-300: var(--color-border-100); /* strong sidebar border- 1 */ - --color-sidebar-border-400: var(--color-border-100); /* strong sidebar border- 2 */ + --color-sidebar-border-200: var(--color-border-200); /* subtle sidebar border- 2 */ + --color-sidebar-border-300: var(--color-border-300); /* strong sidebar border- 1 */ + --color-sidebar-border-400: var(--color-border-400); /* strong sidebar border- 2 */ } } diff --git a/turbo.json b/turbo.json index 62afa90bb..7c3ccb81a 100644 --- a/turbo.json +++ b/turbo.json @@ -22,7 +22,8 @@ "SLACK_CLIENT_SECRET", "JITSU_TRACKER_ACCESS_KEY", "JITSU_TRACKER_HOST", - "UNSPLASH_ACCESS_KEY" + "UNSPLASH_ACCESS_KEY", + "NEXT_PUBLIC_SLACK_CLIENT_ID" ], "pipeline": { "build": { diff --git a/web/components/dnd/StrictModeDroppable.tsx b/web/components/dnd/StrictModeDroppable.tsx index 9ed01d3bf..9feba79b2 100644 --- a/web/components/dnd/StrictModeDroppable.tsx +++ b/web/components/dnd/StrictModeDroppable.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from "react"; // react beautiful dnd -import { Droppable, DroppableProps } from "react-beautiful-dnd"; +import { Droppable, DroppableProps } from "@hello-pangea/dnd"; const StrictModeDroppable = ({ children, ...props }: DroppableProps) => { const [enabled, setEnabled] = useState(false); diff --git a/web/components/gantt-chart/cycle-sidebar.tsx b/web/components/gantt-chart/cycle-sidebar.tsx index 4b0b654f2..5ecc1f3ba 100644 --- a/web/components/gantt-chart/cycle-sidebar.tsx +++ b/web/components/gantt-chart/cycle-sidebar.tsx @@ -1,5 +1,5 @@ import { useRouter } from "next/router"; -import { DragDropContext, Draggable, DropResult } from "react-beautiful-dnd"; +import { DragDropContext, Draggable, DropResult } from "@hello-pangea/dnd"; import StrictModeDroppable from "components/dnd/StrictModeDroppable"; import { MoreVertical } from "lucide-react"; // hooks diff --git a/web/components/gantt-chart/module-sidebar.tsx b/web/components/gantt-chart/module-sidebar.tsx index 4b0b654f2..5ecc1f3ba 100644 --- a/web/components/gantt-chart/module-sidebar.tsx +++ b/web/components/gantt-chart/module-sidebar.tsx @@ -1,5 +1,5 @@ import { useRouter } from "next/router"; -import { DragDropContext, Draggable, DropResult } from "react-beautiful-dnd"; +import { DragDropContext, Draggable, DropResult } from "@hello-pangea/dnd"; import StrictModeDroppable from "components/dnd/StrictModeDroppable"; import { MoreVertical } from "lucide-react"; // hooks diff --git a/web/components/gantt-chart/sidebar.tsx b/web/components/gantt-chart/sidebar.tsx index 72da2a4bd..f6c32e099 100644 --- a/web/components/gantt-chart/sidebar.tsx +++ b/web/components/gantt-chart/sidebar.tsx @@ -1,5 +1,5 @@ import { useRouter } from "next/router"; -import { DragDropContext, Draggable, DropResult } from "react-beautiful-dnd"; +import { DragDropContext, Draggable, DropResult } from "@hello-pangea/dnd"; import StrictModeDroppable from "components/dnd/StrictModeDroppable"; import { MoreVertical } from "lucide-react"; // hooks diff --git a/web/components/issues/comment/add-comment.tsx b/web/components/issues/comment/add-comment.tsx index 71294b8c5..28c986bc7 100644 --- a/web/components/issues/comment/add-comment.tsx +++ b/web/components/issues/comment/add-comment.tsx @@ -50,9 +50,9 @@ export const AddComment: React.FC = ({ disabled = false, onSubmit, showAc const editorRef = React.useRef(null); const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const { workspaceSlug } = router.query; - const editorSuggestions = useEditorSuggestions(workspaceSlug as string | undefined, projectId as string | undefined); + const editorSuggestions = useEditorSuggestions(); const { control, @@ -88,7 +88,8 @@ export const AddComment: React.FC = ({ disabled = false, onSubmit, showAc deleteFile={fileService.deleteImage} ref={editorRef} value={!commentValue || commentValue === "" ? "

" : commentValue} - customClassName="p-3 min-h-[100px] shadow-sm" + customClassName="p-2 h-full" + editorContentCustomClassNames="min-h-[35px]" debouncedUpdatesEnabled={false} onChange={(comment_json: Object, comment_html: string) => onCommentChange(comment_html)} commentAccessSpecifier={ @@ -98,15 +99,21 @@ export const AddComment: React.FC = ({ disabled = false, onSubmit, showAc } mentionSuggestions={editorSuggestions.mentionSuggestions} mentionHighlights={editorSuggestions.mentionHighlights} + submitButton={ + + } /> )} /> )} /> - -
diff --git a/web/components/issues/comment/comment-card.tsx b/web/components/issues/comment/comment-card.tsx index e409122e9..d967fd357 100644 --- a/web/components/issues/comment/comment-card.tsx +++ b/web/components/issues/comment/comment-card.tsx @@ -40,7 +40,7 @@ export const CommentCard: React.FC = ({ const editorRef = React.useRef(null); const showEditorRef = React.useRef(null); - const editorSuggestions = useEditorSuggestions(workspaceSlug, comment.project_detail.id) + const editorSuggestions = useEditorSuggestions(); const [isEditing, setIsEditing] = useState(false); diff --git a/web/components/issues/description-form.tsx b/web/components/issues/description-form.tsx index 6dd569f8f..a3c4289f0 100644 --- a/web/components/issues/description-form.tsx +++ b/web/components/issues/description-form.tsx @@ -38,7 +38,7 @@ export const IssueDescriptionForm: FC = (props) => { const { setShowAlert } = useReloadConfirmations(); - const editorSuggestion = useEditorSuggestions(workspaceSlug, issue.project_id) + const editorSuggestion = useEditorSuggestions(); const { handleSubmit, @@ -164,8 +164,9 @@ export const IssueDescriptionForm: FC = (props) => { )} />
{isSubmitting === "submitting" ? "Saving..." : "Saved"}
diff --git a/web/components/issues/draft-issue-form.tsx b/web/components/issues/draft-issue-form.tsx index 6b1af9719..acce0fb79 100644 --- a/web/components/issues/draft-issue-form.tsx +++ b/web/components/issues/draft-issue-form.tsx @@ -119,7 +119,7 @@ export const DraftIssueForm: FC = (props) => { const { setToastAlert } = useToast(); - const editorSuggestions = useEditorSuggestions(workspaceSlug as string | undefined, projectId); + const editorSuggestions = useEditorSuggestions(); const { formState: { errors, isSubmitting, isDirty }, diff --git a/web/components/issues/form.tsx b/web/components/issues/form.tsx index d6de423ed..94bcc09bc 100644 --- a/web/components/issues/form.tsx +++ b/web/components/issues/form.tsx @@ -112,7 +112,7 @@ export const IssueForm: FC = observer((props) => { const user = userStore.currentUser; - const editorSuggestion = useEditorSuggestions(workspaceSlug as string | undefined, projectId); + const editorSuggestion = useEditorSuggestions(); const { setToastAlert } = useToast(); diff --git a/web/components/issues/issue-layouts/empty-states/cycle.tsx b/web/components/issues/issue-layouts/empty-states/cycle.tsx index 642e1dad9..53ea45001 100644 --- a/web/components/issues/issue-layouts/empty-states/cycle.tsx +++ b/web/components/issues/issue-layouts/empty-states/cycle.tsx @@ -1,38 +1,85 @@ +import { useState } from "react"; +import { observer } from "mobx-react-lite"; import { PlusIcon } from "lucide-react"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import useToast from "hooks/use-toast"; // components import { EmptyState } from "components/common"; +import { ExistingIssuesListModal } from "components/core"; +// ui +import { Button } from "@plane/ui"; // assets import emptyIssue from "public/empty-state/issue.svg"; -import { Button } from "@plane/ui"; +// types +import { ISearchIssueResponse } from "types"; type Props = { - openIssuesListModal: () => void; + workspaceSlug: string | undefined; + projectId: string | undefined; + cycleId: string | undefined; }; -export const CycleEmptyState: React.FC = ({ openIssuesListModal }) => ( -
- , - onClick: () => { - const e = new KeyboardEvent("keydown", { - key: "c", - }); - document.dispatchEvent(e); - }, - }} - secondaryButton={ - - } - /> -
-); +export const CycleEmptyState: React.FC = observer((props) => { + const { workspaceSlug, projectId, cycleId } = props; + // states + const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false); + + const { cycleIssue: cycleIssueStore } = useMobxStore(); + + const { setToastAlert } = useToast(); + + const handleAddIssuesToCycle = async (data: ISearchIssueResponse[]) => { + if (!workspaceSlug || !projectId || !cycleId) return; + + const issueIds = data.map((i) => i.id); + + await cycleIssueStore + .addIssueToCycle(workspaceSlug.toString(), projectId.toString(), cycleId.toString(), issueIds) + .catch(() => { + setToastAlert({ + type: "error", + title: "Error!", + message: "Selected issues could not be added to the cycle. Please try again.", + }); + }); + }; + + return ( + <> + setCycleIssuesListModal(false)} + searchParams={{ cycle: true }} + handleOnSubmit={handleAddIssuesToCycle} + /> +
+ , + onClick: () => { + const e = new KeyboardEvent("keydown", { + key: "c", + }); + document.dispatchEvent(e); + }, + }} + secondaryButton={ + + } + /> +
+ + ); +}); diff --git a/web/components/issues/issue-layouts/empty-states/module.tsx b/web/components/issues/issue-layouts/empty-states/module.tsx index a71be523f..7e5edeeb8 100644 --- a/web/components/issues/issue-layouts/empty-states/module.tsx +++ b/web/components/issues/issue-layouts/empty-states/module.tsx @@ -4,36 +4,78 @@ import { EmptyState } from "components/common"; import { Button } from "@plane/ui"; // assets import emptyIssue from "public/empty-state/issue.svg"; +import { ExistingIssuesListModal } from "components/core"; +import { observer } from "mobx-react-lite"; +import { useMobxStore } from "lib/mobx/store-provider"; +import { ISearchIssueResponse } from "types"; +import useToast from "hooks/use-toast"; +import { useState } from "react"; type Props = { - openIssuesListModal: () => void; + workspaceSlug: string | undefined; + projectId: string | undefined; + moduleId: string | undefined; }; -export const ModuleEmptyState: React.FC = ({ openIssuesListModal }) => ( -
- , - onClick: () => { - const e = new KeyboardEvent("keydown", { - key: "c", - }); - document.dispatchEvent(e); - }, - }} - secondaryButton={ - - } - /> -
-); +export const ModuleEmptyState: React.FC = observer((props) => { + const { workspaceSlug, projectId, moduleId } = props; + // states + const [moduleIssuesListModal, setModuleIssuesListModal] = useState(false); + + const { moduleIssue: moduleIssueStore } = useMobxStore(); + + const { setToastAlert } = useToast(); + + const handleAddIssuesToModule = async (data: ISearchIssueResponse[]) => { + if (!workspaceSlug || !projectId || !moduleId) return; + + const issueIds = data.map((i) => i.id); + + await moduleIssueStore + .addIssueToModule(workspaceSlug.toString(), projectId.toString(), moduleId.toString(), issueIds) + .catch(() => + setToastAlert({ + type: "error", + title: "Error!", + message: "Selected issues could not be added to the module. Please try again.", + }) + ); + }; + + return ( + <> + setModuleIssuesListModal(false)} + searchParams={{ module: true }} + handleOnSubmit={handleAddIssuesToModule} + /> +
+ , + onClick: () => { + const e = new KeyboardEvent("keydown", { + key: "c", + }); + document.dispatchEvent(e); + }, + }} + secondaryButton={ + + } + /> +
+ + ); +}); diff --git a/web/components/issues/issue-layouts/kanban/block.tsx b/web/components/issues/issue-layouts/kanban/block.tsx index 34ed02382..9d4c8aedd 100644 --- a/web/components/issues/issue-layouts/kanban/block.tsx +++ b/web/components/issues/issue-layouts/kanban/block.tsx @@ -42,7 +42,7 @@ export const KanbanIssueBlock: React.FC = (props) => { return ( <> - + {(provided, snapshot) => (
= (props) => { )}
diff --git a/web/components/issues/issue-layouts/kanban/default.tsx b/web/components/issues/issue-layouts/kanban/default.tsx index 1e252895b..eee31e7c3 100644 --- a/web/components/issues/issue-layouts/kanban/default.tsx +++ b/web/components/issues/issue-layouts/kanban/default.tsx @@ -10,11 +10,13 @@ import { KanbanIssueBlocksList, BoardInlineCreateIssueForm } from "components/is import { IIssueDisplayProperties, IIssue } from "types"; // constants import { getValueFromObject } from "constants/issue"; +import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper"; export interface IGroupByKanBan { issues: any; sub_group_by: string | null; group_by: string | null; + order_by: string | null; sub_group_id: string; list: any; listKey: string; @@ -31,6 +33,7 @@ export interface IGroupByKanBan { kanBanToggle: any; handleKanBanToggle: any; enableQuickIssueCreate?: boolean; + isDragStarted?: boolean; } const GroupByKanBan: React.FC = observer((props) => { @@ -38,6 +41,7 @@ const GroupByKanBan: React.FC = observer((props) => { issues, sub_group_by, group_by, + order_by, sub_group_id = "null", list, listKey, @@ -49,17 +53,20 @@ const GroupByKanBan: React.FC = observer((props) => { kanBanToggle, handleKanBanToggle, enableQuickIssueCreate, + isDragStarted, } = props; const verticalAlignPosition = (_list: any) => kanBanToggle?.groupByHeaderMinMax.includes(getValueFromObject(_list, listKey) as string); return ( -
+
{list && list.length > 0 && list.map((_list: any) => ( -
+
{sub_group_by === null && (
= observer((props) => { verticalAlignPosition(_list) ? `w-[0px] overflow-hidden` : `w-full transition-all` }`} > - + {(provided: any, snapshot: any) => (
= observer((props) => { /> ) : ( isDragDisabled && ( -
+
{/*
Drop here
*/}
) )} + {provided.placeholder}
)}
- {enableQuickIssueCreate && ( - + +
+ {enableQuickIssueCreate && ( + + )} +
+ + {isDragStarted && isDragDisabled && ( +
+
+ {`This board is ordered by "${replaceUnderscoreIfSnakeCase( + order_by ? (order_by[0] === "-" ? order_by.slice(1) : order_by) : "created_at" + )}"`} +
+
)}
))} @@ -131,8 +155,8 @@ export interface IKanBan { issues: any; sub_group_by: string | null; group_by: string | null; + order_by: string | null; sub_group_id?: string; - handleDragDrop?: (result: any) => void | undefined; handleIssues: ( sub_group_by: string | null, group_by: string | null, @@ -151,6 +175,7 @@ export interface IKanBan { members: any; projects: any; enableQuickIssueCreate?: boolean; + isDragStarted?: boolean; } export const KanBan: React.FC = observer((props) => { @@ -158,6 +183,7 @@ export const KanBan: React.FC = observer((props) => { issues, sub_group_by, group_by, + order_by, sub_group_id = "null", handleIssues, quickActions, @@ -172,6 +198,7 @@ export const KanBan: React.FC = observer((props) => { members, projects, enableQuickIssueCreate, + isDragStarted, } = props; const { issueKanBanView: issueKanBanViewStore } = useMobxStore(); @@ -182,6 +209,7 @@ export const KanBan: React.FC = observer((props) => { = observer((props) => { kanBanToggle={kanBanToggle} handleKanBanToggle={handleKanBanToggle} enableQuickIssueCreate={enableQuickIssueCreate} + isDragStarted={isDragStarted} /> )} @@ -201,6 +230,7 @@ export const KanBan: React.FC = observer((props) => { = observer((props) => { kanBanToggle={kanBanToggle} handleKanBanToggle={handleKanBanToggle} enableQuickIssueCreate={enableQuickIssueCreate} + isDragStarted={isDragStarted} /> )} @@ -220,6 +251,7 @@ export const KanBan: React.FC = observer((props) => { = observer((props) => { kanBanToggle={kanBanToggle} handleKanBanToggle={handleKanBanToggle} enableQuickIssueCreate={enableQuickIssueCreate} + isDragStarted={isDragStarted} /> )} @@ -239,6 +272,7 @@ export const KanBan: React.FC = observer((props) => { = observer((props) => { kanBanToggle={kanBanToggle} handleKanBanToggle={handleKanBanToggle} enableQuickIssueCreate={enableQuickIssueCreate} + isDragStarted={isDragStarted} /> )} @@ -258,6 +293,7 @@ export const KanBan: React.FC = observer((props) => { = observer((props) => { kanBanToggle={kanBanToggle} handleKanBanToggle={handleKanBanToggle} enableQuickIssueCreate={enableQuickIssueCreate} + isDragStarted={isDragStarted} /> )} @@ -277,6 +314,7 @@ export const KanBan: React.FC = observer((props) => { = observer((props) => { kanBanToggle={kanBanToggle} handleKanBanToggle={handleKanBanToggle} enableQuickIssueCreate={enableQuickIssueCreate} + isDragStarted={isDragStarted} /> )} @@ -296,6 +335,7 @@ export const KanBan: React.FC = observer((props) => { = observer((props) => { kanBanToggle={kanBanToggle} handleKanBanToggle={handleKanBanToggle} enableQuickIssueCreate={enableQuickIssueCreate} + isDragStarted={isDragStarted} /> )}
diff --git a/web/components/issues/issue-layouts/kanban/headers/state-group.tsx b/web/components/issues/issue-layouts/kanban/headers/state-group.tsx index 33c70bddd..47d258c36 100644 --- a/web/components/issues/issue-layouts/kanban/headers/state-group.tsx +++ b/web/components/issues/issue-layouts/kanban/headers/state-group.tsx @@ -17,7 +17,7 @@ export interface IStateGroupHeader { } export const Icon = ({ stateGroup, color }: { stateGroup: any; color?: any }) => ( -
+
); diff --git a/web/components/issues/issue-layouts/kanban/roots/cycle-root.tsx b/web/components/issues/issue-layouts/kanban/roots/cycle-root.tsx index 82f4920c0..facf2ec94 100644 --- a/web/components/issues/issue-layouts/kanban/roots/cycle-root.tsx +++ b/web/components/issues/issue-layouts/kanban/roots/cycle-root.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from "react"; +import React, { useCallback, useState } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { DragDropContext } from "@hello-pangea/dnd"; @@ -8,6 +8,7 @@ import { useMobxStore } from "lib/mobx/store-provider"; import { KanBanSwimLanes } from "../swimlanes"; import { KanBan } from "../default"; import { CycleIssueQuickActions } from "components/issues"; +import { Spinner } from "@plane/ui"; // helpers import { orderArrayBy } from "helpers/array.helper"; // types @@ -37,6 +38,8 @@ export const CycleKanBanLayout: React.FC = observer(() => { const group_by: string | null = issueFilterStore?.userDisplayFilters?.group_by || null; + const order_by: string | null = issueFilterStore?.userDisplayFilters?.order_by || null; + const userDisplayFilters = issueFilterStore?.userDisplayFilters || null; const displayProperties = issueFilterStore?.userDisplayProperties || null; @@ -45,7 +48,15 @@ export const CycleKanBanLayout: React.FC = observer(() => { ? "swimlanes" : "default"; + const [isDragStarted, setIsDragStarted] = useState(false); + + const onDragStart = () => { + setIsDragStarted(true); + }; + const onDragEnd = (result: any) => { + setIsDragStarted(false); + if (!result) return; if ( @@ -99,60 +110,72 @@ export const CycleKanBanLayout: React.FC = observer(() => { : null; return ( -
- - {currentKanBanView === "default" ? ( - ( - handleIssues(sub_group_by, group_by, issue, "delete")} - handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")} - handleRemoveFromCycle={async () => handleIssues(sub_group_by, group_by, issue, "remove")} + <> + {cycleIssueStore.loader ? ( +
+ +
+ ) : ( +
+ + {currentKanBanView === "default" ? ( + ( + handleIssues(sub_group_by, group_by, issue, "delete")} + handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")} + handleRemoveFromCycle={async () => handleIssues(sub_group_by, group_by, issue, "remove")} + /> + )} + displayProperties={displayProperties} + kanBanToggle={cycleIssueKanBanViewStore?.kanBanToggle} + handleKanBanToggle={handleKanBanToggle} + states={states} + stateGroups={stateGroups} + priorities={priorities} + labels={labels} + members={members?.map((m) => m.member) ?? null} + projects={projects} + showEmptyGroup={userDisplayFilters?.show_empty_groups || true} + isDragStarted={isDragStarted} + /> + ) : ( + ( + handleIssues(sub_group_by, group_by, issue, "delete")} + handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")} + handleRemoveFromCycle={async () => handleIssues(sub_group_by, group_by, issue, "remove")} + /> + )} + displayProperties={displayProperties} + kanBanToggle={cycleIssueKanBanViewStore?.kanBanToggle} + handleKanBanToggle={handleKanBanToggle} + states={states} + stateGroups={stateGroups} + priorities={priorities} + labels={labels} + members={members?.map((m) => m.member) ?? null} + projects={projects} + showEmptyGroup={userDisplayFilters?.show_empty_groups || true} + isDragStarted={isDragStarted} /> )} - displayProperties={displayProperties} - kanBanToggle={cycleIssueKanBanViewStore?.kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - states={states} - stateGroups={stateGroups} - priorities={priorities} - labels={labels} - members={members?.map((m) => m.member) ?? null} - projects={projects} - showEmptyGroup={userDisplayFilters?.show_empty_groups || true} - /> - ) : ( - ( - handleIssues(sub_group_by, group_by, issue, "delete")} - handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")} - handleRemoveFromCycle={async () => handleIssues(sub_group_by, group_by, issue, "remove")} - /> - )} - displayProperties={displayProperties} - kanBanToggle={cycleIssueKanBanViewStore?.kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - states={states} - stateGroups={stateGroups} - priorities={priorities} - labels={labels} - members={members?.map((m) => m.member) ?? null} - projects={projects} - showEmptyGroup={userDisplayFilters?.show_empty_groups || true} - /> - )} - -
+
+
+ )} + ); }); diff --git a/web/components/issues/issue-layouts/kanban/roots/module-root.tsx b/web/components/issues/issue-layouts/kanban/roots/module-root.tsx index be40c6bca..c37cc5550 100644 --- a/web/components/issues/issue-layouts/kanban/roots/module-root.tsx +++ b/web/components/issues/issue-layouts/kanban/roots/module-root.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from "react"; +import React, { useCallback, useState } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { DragDropContext } from "@hello-pangea/dnd"; @@ -8,6 +8,7 @@ import { useMobxStore } from "lib/mobx/store-provider"; import { KanBanSwimLanes } from "../swimlanes"; import { KanBan } from "../default"; import { ModuleIssueQuickActions } from "components/issues"; +import { Spinner } from "@plane/ui"; // helpers import { orderArrayBy } from "helpers/array.helper"; // types @@ -36,6 +37,8 @@ export const ModuleKanBanLayout: React.FC = observer(() => { const group_by: string | null = issueFilterStore?.userDisplayFilters?.group_by || null; + const order_by: string | null = issueFilterStore?.userDisplayFilters?.order_by || null; + const userDisplayFilters = issueFilterStore?.userDisplayFilters || null; const displayProperties = issueFilterStore?.userDisplayProperties || null; @@ -44,7 +47,14 @@ export const ModuleKanBanLayout: React.FC = observer(() => { ? "swimlanes" : "default"; + const [isDragStarted, setIsDragStarted] = useState(false); + + const onDragStart = () => { + setIsDragStarted(true); + }; + const onDragEnd = (result: any) => { + setIsDragStarted(false); if (!result) return; if ( @@ -98,60 +108,72 @@ export const ModuleKanBanLayout: React.FC = observer(() => { : null; return ( -
- - {currentKanBanView === "default" ? ( - ( - handleIssues(sub_group_by, group_by, issue, "delete")} - handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")} - handleRemoveFromModule={async () => handleIssues(sub_group_by, group_by, issue, "remove")} + <> + {moduleIssueStore.loader ? ( +
+ +
+ ) : ( +
+ + {currentKanBanView === "default" ? ( + ( + handleIssues(sub_group_by, group_by, issue, "delete")} + handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")} + handleRemoveFromModule={async () => handleIssues(sub_group_by, group_by, issue, "remove")} + /> + )} + displayProperties={displayProperties} + kanBanToggle={moduleIssueKanBanViewStore?.kanBanToggle} + handleKanBanToggle={handleKanBanToggle} + states={states} + stateGroups={stateGroups} + priorities={priorities} + labels={labels} + members={members?.map((m) => m.member) ?? null} + projects={projects} + showEmptyGroup={userDisplayFilters?.show_empty_groups || true} + isDragStarted={isDragStarted} + /> + ) : ( + ( + handleIssues(sub_group_by, group_by, issue, "delete")} + handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")} + handleRemoveFromModule={async () => handleIssues(sub_group_by, group_by, issue, "remove")} + /> + )} + displayProperties={displayProperties} + kanBanToggle={moduleIssueKanBanViewStore?.kanBanToggle} + handleKanBanToggle={handleKanBanToggle} + states={states} + stateGroups={stateGroups} + priorities={priorities} + labels={labels} + members={members?.map((m) => m.member) ?? null} + projects={projects} + showEmptyGroup={userDisplayFilters?.show_empty_groups || true} + isDragStarted={isDragStarted} /> )} - displayProperties={displayProperties} - kanBanToggle={moduleIssueKanBanViewStore?.kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - states={states} - stateGroups={stateGroups} - priorities={priorities} - labels={labels} - members={members?.map((m) => m.member) ?? null} - projects={projects} - showEmptyGroup={userDisplayFilters?.show_empty_groups || true} - /> - ) : ( - ( - handleIssues(sub_group_by, group_by, issue, "delete")} - handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")} - handleRemoveFromModule={async () => handleIssues(sub_group_by, group_by, issue, "remove")} - /> - )} - displayProperties={displayProperties} - kanBanToggle={moduleIssueKanBanViewStore?.kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - states={states} - stateGroups={stateGroups} - priorities={priorities} - labels={labels} - members={members?.map((m) => m.member) ?? null} - projects={projects} - showEmptyGroup={userDisplayFilters?.show_empty_groups || true} - /> - )} - -
+
+
+ )} + ); }); diff --git a/web/components/issues/issue-layouts/kanban/roots/profile-issues-root.tsx b/web/components/issues/issue-layouts/kanban/roots/profile-issues-root.tsx index 2849315b4..8d0cf1729 100644 --- a/web/components/issues/issue-layouts/kanban/roots/profile-issues-root.tsx +++ b/web/components/issues/issue-layouts/kanban/roots/profile-issues-root.tsx @@ -1,4 +1,4 @@ -import { FC, useCallback } from "react"; +import { FC, useCallback, useState } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { DragDropContext } from "@hello-pangea/dnd"; @@ -8,6 +8,7 @@ import { useMobxStore } from "lib/mobx/store-provider"; import { KanBanSwimLanes } from "../swimlanes"; import { KanBan } from "../default"; import { ProjectIssueQuickActions } from "components/issues"; +import { Spinner } from "@plane/ui"; // constants import { ISSUE_STATE_GROUPS, ISSUE_PRIORITIES } from "constants/issue"; // types @@ -34,6 +35,8 @@ export const ProfileIssuesKanBanLayout: FC = observer(() => { const group_by: string | null = profileIssueFiltersStore?.userDisplayFilters?.group_by || null; + const order_by: string | null = profileIssueFiltersStore?.userDisplayFilters?.order_by || null; + const userDisplayFilters = profileIssueFiltersStore?.userDisplayFilters || null; const displayProperties = profileIssueFiltersStore?.userDisplayProperties || null; @@ -42,7 +45,14 @@ export const ProfileIssuesKanBanLayout: FC = observer(() => { ? "swimlanes" : "default"; + const [isDragStarted, setIsDragStarted] = useState(false); + + const onDragStart = () => { + setIsDragStarted(true); + }; + const onDragEnd = (result: any) => { + setIsDragStarted(false); if (!result) return; if ( @@ -83,58 +93,70 @@ export const ProfileIssuesKanBanLayout: FC = observer(() => { const projects = projectStore?.workspaceProjects || null; return ( -
- - {currentKanBanView === "default" ? ( - ( - handleIssues(sub_group_by, group_by, issue, "delete")} - handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")} + <> + {profileIssuesStore.loader ? ( +
+ +
+ ) : ( +
+ + {currentKanBanView === "default" ? ( + ( + handleIssues(sub_group_by, group_by, issue, "delete")} + handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")} + /> + )} + displayProperties={displayProperties} + kanBanToggle={issueKanBanViewStore?.kanBanToggle} + handleKanBanToggle={handleKanBanToggle} + states={states} + stateGroups={stateGroups} + priorities={priorities} + labels={labels} + members={members?.map((m) => m.member) ?? null} + projects={projects} + showEmptyGroup={userDisplayFilters?.show_empty_groups || true} + isDragStarted={isDragStarted} + /> + ) : ( + ( + handleIssues(sub_group_by, group_by, issue, "delete")} + handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")} + /> + )} + displayProperties={displayProperties} + kanBanToggle={issueKanBanViewStore?.kanBanToggle} + handleKanBanToggle={handleKanBanToggle} + states={states} + stateGroups={stateGroups} + priorities={priorities} + labels={labels} + members={members?.map((m) => m.member) ?? null} + projects={projects} + showEmptyGroup={userDisplayFilters?.show_empty_groups || true} + isDragStarted={isDragStarted} /> )} - displayProperties={displayProperties} - kanBanToggle={issueKanBanViewStore?.kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - states={states} - stateGroups={stateGroups} - priorities={priorities} - labels={labels} - members={members?.map((m) => m.member) ?? null} - projects={projects} - showEmptyGroup={userDisplayFilters?.show_empty_groups || true} - /> - ) : ( - ( - handleIssues(sub_group_by, group_by, issue, "delete")} - handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")} - /> - )} - displayProperties={displayProperties} - kanBanToggle={issueKanBanViewStore?.kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - states={states} - stateGroups={stateGroups} - priorities={priorities} - labels={labels} - members={members?.map((m) => m.member) ?? null} - projects={projects} - showEmptyGroup={userDisplayFilters?.show_empty_groups || true} - /> - )} - -
+
+
+ )} + ); }); diff --git a/web/components/issues/issue-layouts/kanban/roots/project-root.tsx b/web/components/issues/issue-layouts/kanban/roots/project-root.tsx index 179d628b4..52e60813e 100644 --- a/web/components/issues/issue-layouts/kanban/roots/project-root.tsx +++ b/web/components/issues/issue-layouts/kanban/roots/project-root.tsx @@ -1,4 +1,4 @@ -import { useCallback } from "react"; +import { useCallback, useState } from "react"; import { useRouter } from "next/router"; import { DragDropContext } from "@hello-pangea/dnd"; import { observer } from "mobx-react-lite"; @@ -8,6 +8,7 @@ import { useMobxStore } from "lib/mobx/store-provider"; import { KanBanSwimLanes } from "../swimlanes"; import { KanBan } from "../default"; import { ProjectIssueQuickActions } from "components/issues"; +import { Spinner } from "@plane/ui"; // types import { IIssue } from "types"; // constants @@ -34,6 +35,8 @@ export const KanBanLayout: React.FC = observer(() => { const group_by: string | null = issueFilterStore?.userDisplayFilters?.group_by || null; + const order_by: string | null = issueFilterStore?.userDisplayFilters?.order_by || null; + const userDisplayFilters = issueFilterStore?.userDisplayFilters || null; const displayProperties = issueFilterStore?.userDisplayProperties || null; @@ -42,12 +45,22 @@ export const KanBanLayout: React.FC = observer(() => { ? "swimlanes" : "default"; + const [isDragStarted, setIsDragStarted] = useState(false); + + const onDragStart = () => { + setIsDragStarted(true); + }; + const onDragEnd = (result: any) => { + setIsDragStarted(false); + if (!result) return; if ( result.destination && result.source && + result.source.droppableId && + result.destination.droppableId && result.destination.droppableId === result.source.droppableId && result.destination.index === result.source.index ) @@ -87,59 +100,71 @@ export const KanBanLayout: React.FC = observer(() => { : null; return ( -
- - {currentKanBanView === "default" ? ( - ( - handleIssues(sub_group_by, group_by, issue, "delete")} - handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")} + <> + {issueStore.loader ? ( +
+ +
+ ) : ( +
+ + {currentKanBanView === "default" ? ( + ( + handleIssues(sub_group_by, group_by, issue, "delete")} + handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")} + /> + )} + displayProperties={displayProperties} + kanBanToggle={issueKanBanViewStore?.kanBanToggle} + handleKanBanToggle={handleKanBanToggle} + states={states} + stateGroups={stateGroups} + priorities={priorities} + labels={labels} + members={members?.map((m) => m.member) ?? null} + projects={projects} + enableQuickIssueCreate + showEmptyGroup={userDisplayFilters?.show_empty_groups || true} + isDragStarted={isDragStarted} + /> + ) : ( + ( + handleIssues(sub_group_by, group_by, issue, "delete")} + handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")} + /> + )} + displayProperties={displayProperties} + kanBanToggle={issueKanBanViewStore?.kanBanToggle} + handleKanBanToggle={handleKanBanToggle} + states={states} + stateGroups={stateGroups} + priorities={priorities} + labels={labels} + members={members?.map((m) => m.member) ?? null} + projects={projects} + showEmptyGroup={userDisplayFilters?.show_empty_groups || true} + isDragStarted={isDragStarted} /> )} - displayProperties={displayProperties} - kanBanToggle={issueKanBanViewStore?.kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - states={states} - stateGroups={stateGroups} - priorities={priorities} - labels={labels} - members={members?.map((m) => m.member) ?? null} - projects={projects} - enableQuickIssueCreate - showEmptyGroup={userDisplayFilters?.show_empty_groups || true} - /> - ) : ( - ( - handleIssues(sub_group_by, group_by, issue, "delete")} - handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")} - /> - )} - displayProperties={displayProperties} - kanBanToggle={issueKanBanViewStore?.kanBanToggle} - handleKanBanToggle={handleKanBanToggle} - states={states} - stateGroups={stateGroups} - priorities={priorities} - labels={labels} - members={members?.map((m) => m.member) ?? null} - projects={projects} - showEmptyGroup={userDisplayFilters?.show_empty_groups || true} - /> - )} - -
+
+
+ )} + ); }); diff --git a/web/components/issues/issue-layouts/kanban/swimlanes.tsx b/web/components/issues/issue-layouts/kanban/swimlanes.tsx index 662a97234..e25ddea3d 100644 --- a/web/components/issues/issue-layouts/kanban/swimlanes.tsx +++ b/web/components/issues/issue-layouts/kanban/swimlanes.tsx @@ -58,6 +58,7 @@ const SubGroupSwimlaneHeader: React.FC = ({ }; interface ISubGroupSwimlane extends ISubGroupSwimlaneHeader { + order_by: string | null; showEmptyGroup: boolean; states: IState[] | null; stateGroups: any; @@ -76,12 +77,14 @@ interface ISubGroupSwimlane extends ISubGroupSwimlaneHeader { displayProperties: IIssueDisplayProperties; kanBanToggle: any; handleKanBanToggle: any; + isDragStarted?: boolean; } const SubGroupSwimlane: React.FC = observer((props) => { const { issues, sub_group_by, group_by, + order_by, list, listKey, handleIssues, @@ -96,6 +99,7 @@ const SubGroupSwimlane: React.FC = observer((props) => { labels, members, projects, + isDragStarted, } = props; const calculateIssueCount = (column_id: string) => { @@ -133,6 +137,7 @@ const SubGroupSwimlane: React.FC = observer((props) => { issues={issues?.[getValueFromObject(_list, listKey) as string]} sub_group_by={sub_group_by} group_by={group_by} + order_by={order_by} sub_group_id={getValueFromObject(_list, listKey) as string} handleIssues={handleIssues} quickActions={quickActions} @@ -147,6 +152,7 @@ const SubGroupSwimlane: React.FC = observer((props) => { members={members} projects={projects} enableQuickIssueCreate + isDragStarted={isDragStarted} />
)} @@ -160,6 +166,7 @@ export interface IKanBanSwimLanes { issues: any; sub_group_by: string | null; group_by: string | null; + order_by: string | null; handleIssues: ( sub_group_by: string | null, group_by: string | null, @@ -177,6 +184,7 @@ export interface IKanBanSwimLanes { labels: IIssueLabels[] | null; members: IUserLite[] | null; projects: IProject[] | null; + isDragStarted?: boolean; } export const KanBanSwimLanes: React.FC = observer((props) => { @@ -184,6 +192,7 @@ export const KanBanSwimLanes: React.FC = observer((props) => { issues, sub_group_by, group_by, + order_by, handleIssues, quickActions, displayProperties, @@ -196,6 +205,7 @@ export const KanBanSwimLanes: React.FC = observer((props) => { labels, members, projects, + isDragStarted, } = props; return ( @@ -291,6 +301,7 @@ export const KanBanSwimLanes: React.FC = observer((props) => { issues={issues} sub_group_by={sub_group_by} group_by={group_by} + order_by={order_by} list={projects} listKey={`id`} handleIssues={handleIssues} @@ -305,6 +316,7 @@ export const KanBanSwimLanes: React.FC = observer((props) => { labels={labels} members={members} projects={projects} + isDragStarted={isDragStarted} /> )} @@ -313,6 +325,7 @@ export const KanBanSwimLanes: React.FC = observer((props) => { issues={issues} sub_group_by={sub_group_by} group_by={group_by} + order_by={order_by} list={states} listKey={`id`} handleIssues={handleIssues} @@ -327,6 +340,7 @@ export const KanBanSwimLanes: React.FC = observer((props) => { labels={labels} members={members} projects={projects} + isDragStarted={isDragStarted} /> )} @@ -335,6 +349,7 @@ export const KanBanSwimLanes: React.FC = observer((props) => { issues={issues} sub_group_by={sub_group_by} group_by={group_by} + order_by={order_by} list={states} listKey={`id`} handleIssues={handleIssues} @@ -349,6 +364,7 @@ export const KanBanSwimLanes: React.FC = observer((props) => { labels={labels} members={members} projects={projects} + isDragStarted={isDragStarted} /> )} @@ -357,6 +373,7 @@ export const KanBanSwimLanes: React.FC = observer((props) => { issues={issues} sub_group_by={sub_group_by} group_by={group_by} + order_by={order_by} list={stateGroups} listKey={`key`} handleIssues={handleIssues} @@ -371,6 +388,7 @@ export const KanBanSwimLanes: React.FC = observer((props) => { labels={labels} members={members} projects={projects} + isDragStarted={isDragStarted} /> )} @@ -379,6 +397,7 @@ export const KanBanSwimLanes: React.FC = observer((props) => { issues={issues} sub_group_by={sub_group_by} group_by={group_by} + order_by={order_by} list={priorities} listKey={`key`} handleIssues={handleIssues} @@ -393,6 +412,7 @@ export const KanBanSwimLanes: React.FC = observer((props) => { labels={labels} members={members} projects={projects} + isDragStarted={isDragStarted} /> )} @@ -401,6 +421,7 @@ export const KanBanSwimLanes: React.FC = observer((props) => { issues={issues} sub_group_by={sub_group_by} group_by={group_by} + order_by={order_by} list={labels ? [...labels, { id: "None", name: "None" }] : labels} listKey={`id`} handleIssues={handleIssues} @@ -415,6 +436,7 @@ export const KanBanSwimLanes: React.FC = observer((props) => { labels={labels} members={members} projects={projects} + isDragStarted={isDragStarted} /> )} @@ -423,6 +445,7 @@ export const KanBanSwimLanes: React.FC = observer((props) => { issues={issues} sub_group_by={sub_group_by} group_by={group_by} + order_by={order_by} list={members ? [...members, { id: "None", display_name: "None" }] : members} listKey={`id`} handleIssues={handleIssues} @@ -437,6 +460,7 @@ export const KanBanSwimLanes: React.FC = observer((props) => { labels={labels} members={members} projects={projects} + isDragStarted={isDragStarted} /> )} @@ -445,6 +469,7 @@ export const KanBanSwimLanes: React.FC = observer((props) => { issues={issues} sub_group_by={sub_group_by} group_by={group_by} + order_by={order_by} list={members} listKey={`id`} handleIssues={handleIssues} @@ -459,6 +484,7 @@ export const KanBanSwimLanes: React.FC = observer((props) => { labels={labels} members={members} projects={projects} + isDragStarted={isDragStarted} /> )}
diff --git a/web/components/issues/issue-layouts/list/block.tsx b/web/components/issues/issue-layouts/list/block.tsx index 52410756a..28f5f765a 100644 --- a/web/components/issues/issue-layouts/list/block.tsx +++ b/web/components/issues/issue-layouts/list/block.tsx @@ -4,20 +4,20 @@ import { IssuePeekOverview } from "components/issues/issue-peek-overview"; // ui import { Tooltip } from "@plane/ui"; // types -import { IIssue } from "types"; +import { IIssue, IIssueDisplayProperties } from "types"; interface IssueBlockProps { columnId: string; issue: IIssue; handleIssues: (group_by: string | null, issue: IIssue, action: "update" | "delete") => void; quickActions: (group_by: string | null, issue: IIssue) => React.ReactNode; - display_properties: any; + displayProperties: IIssueDisplayProperties; isReadonly?: boolean; showEmptyGroup?: boolean; } export const IssueBlock: React.FC = (props) => { - const { columnId, issue, handleIssues, quickActions, display_properties, showEmptyGroup, isReadonly } = props; + const { columnId, issue, handleIssues, quickActions, displayProperties, showEmptyGroup, isReadonly } = props; const updateIssue = (group_by: string | null, issueToUpdate: IIssue) => { handleIssues(group_by, issueToUpdate, "update"); @@ -26,7 +26,7 @@ export const IssueBlock: React.FC = (props) => { return ( <>
- {display_properties && display_properties?.key && ( + {displayProperties && displayProperties?.key && (
{issue?.project_detail?.identifier}-{issue.sequence_id}
@@ -54,7 +54,7 @@ export const IssueBlock: React.FC = (props) => { issue={issue} isReadonly={isReadonly} handleIssues={updateIssue} - display_properties={display_properties} + displayProperties={displayProperties} showEmptyGroup={showEmptyGroup} /> {quickActions(!columnId && columnId === "null" ? null : columnId, issue)} diff --git a/web/components/issues/issue-layouts/list/blocks-list.tsx b/web/components/issues/issue-layouts/list/blocks-list.tsx index 00779cc34..22a92a159 100644 --- a/web/components/issues/issue-layouts/list/blocks-list.tsx +++ b/web/components/issues/issue-layouts/list/blocks-list.tsx @@ -2,7 +2,7 @@ import { FC } from "react"; // components import { IssueBlock } from "components/issues"; // types -import { IIssue } from "types"; +import { IIssue, IIssueDisplayProperties } from "types"; interface Props { columnId: string; @@ -10,12 +10,12 @@ interface Props { isReadonly?: boolean; handleIssues: (group_by: string | null, issue: IIssue, action: "update" | "delete") => void; quickActions: (group_by: string | null, issue: IIssue) => React.ReactNode; - display_properties: any; + displayProperties: IIssueDisplayProperties; showEmptyGroup?: boolean; } export const IssueBlocksList: FC = (props) => { - const { columnId, issues, handleIssues, quickActions, display_properties, showEmptyGroup, isReadonly } = props; + const { columnId, issues, handleIssues, quickActions, displayProperties, showEmptyGroup, isReadonly } = props; return (
@@ -28,7 +28,7 @@ export const IssueBlocksList: FC = (props) => { handleIssues={handleIssues} quickActions={quickActions} isReadonly={isReadonly} - display_properties={display_properties} + displayProperties={displayProperties} showEmptyGroup={showEmptyGroup} /> )) diff --git a/web/components/issues/issue-layouts/list/default.tsx b/web/components/issues/issue-layouts/list/default.tsx index 57cdeb34b..b9d13a92b 100644 --- a/web/components/issues/issue-layouts/list/default.tsx +++ b/web/components/issues/issue-layouts/list/default.tsx @@ -4,7 +4,7 @@ import { observer } from "mobx-react-lite"; import { ListGroupByHeaderRoot } from "./headers/group-by-root"; import { IssueBlocksList, ListInlineCreateIssueForm } from "components/issues"; // types -import { IEstimatePoint, IIssue, IIssueLabels, IProject, IState, IUserLite } from "types"; +import { IEstimatePoint, IIssue, IIssueDisplayProperties, IIssueLabels, IProject, IState, IUserLite } from "types"; // constants import { getValueFromObject } from "constants/issue"; @@ -16,7 +16,7 @@ export interface IGroupByList { listKey: string; handleIssues: (group_by: string | null, issue: IIssue, action: "update" | "delete") => void; quickActions: (group_by: string | null, issue: IIssue) => React.ReactNode; - display_properties: any; + displayProperties: IIssueDisplayProperties; is_list?: boolean; enableQuickIssueCreate?: boolean; showEmptyGroup?: boolean; @@ -31,7 +31,7 @@ const GroupByList: React.FC = observer((props) => { listKey, handleIssues, quickActions, - display_properties, + displayProperties, is_list = false, enableQuickIssueCreate, showEmptyGroup, @@ -59,7 +59,7 @@ const GroupByList: React.FC = observer((props) => { issues={is_list ? issues : issues[getValueFromObject(_list, listKey) as string]} handleIssues={handleIssues} quickActions={quickActions} - display_properties={display_properties} + displayProperties={displayProperties} isReadonly={isReadonly} showEmptyGroup={showEmptyGroup} /> @@ -86,7 +86,7 @@ export interface IList { handleDragDrop?: (result: any) => void | undefined; handleIssues: (group_by: string | null, issue: IIssue, action: "update" | "delete") => void; quickActions: (group_by: string | null, issue: IIssue) => React.ReactNode; - display_properties: any; + displayProperties: IIssueDisplayProperties; states: IState[] | null; labels: IIssueLabels[] | null; members: IUserLite[] | null; @@ -105,7 +105,7 @@ export const List: React.FC = observer((props) => { isReadonly, handleIssues, quickActions, - display_properties, + displayProperties, states, labels, members, @@ -126,7 +126,7 @@ export const List: React.FC = observer((props) => { listKey={`id`} handleIssues={handleIssues} quickActions={quickActions} - display_properties={display_properties} + displayProperties={displayProperties} is_list enableQuickIssueCreate={enableQuickIssueCreate} isReadonly={isReadonly} @@ -142,7 +142,7 @@ export const List: React.FC = observer((props) => { listKey={`id`} handleIssues={handleIssues} quickActions={quickActions} - display_properties={display_properties} + displayProperties={displayProperties} enableQuickIssueCreate={enableQuickIssueCreate} isReadonly={isReadonly} showEmptyGroup={showEmptyGroup} @@ -157,7 +157,7 @@ export const List: React.FC = observer((props) => { listKey={`id`} handleIssues={handleIssues} quickActions={quickActions} - display_properties={display_properties} + displayProperties={displayProperties} enableQuickIssueCreate={enableQuickIssueCreate} isReadonly={isReadonly} showEmptyGroup={showEmptyGroup} @@ -172,7 +172,7 @@ export const List: React.FC = observer((props) => { listKey={`key`} handleIssues={handleIssues} quickActions={quickActions} - display_properties={display_properties} + displayProperties={displayProperties} enableQuickIssueCreate={enableQuickIssueCreate} isReadonly={isReadonly} showEmptyGroup={showEmptyGroup} @@ -187,7 +187,7 @@ export const List: React.FC = observer((props) => { listKey={`key`} handleIssues={handleIssues} quickActions={quickActions} - display_properties={display_properties} + displayProperties={displayProperties} enableQuickIssueCreate={enableQuickIssueCreate} isReadonly={isReadonly} showEmptyGroup={showEmptyGroup} @@ -202,7 +202,7 @@ export const List: React.FC = observer((props) => { listKey={`id`} handleIssues={handleIssues} quickActions={quickActions} - display_properties={display_properties} + displayProperties={displayProperties} enableQuickIssueCreate={enableQuickIssueCreate} isReadonly={isReadonly} showEmptyGroup={showEmptyGroup} @@ -217,7 +217,7 @@ export const List: React.FC = observer((props) => { listKey={`id`} handleIssues={handleIssues} quickActions={quickActions} - display_properties={display_properties} + displayProperties={displayProperties} enableQuickIssueCreate={enableQuickIssueCreate} isReadonly={isReadonly} showEmptyGroup={showEmptyGroup} @@ -232,7 +232,7 @@ export const List: React.FC = observer((props) => { listKey={`id`} handleIssues={handleIssues} quickActions={quickActions} - display_properties={display_properties} + displayProperties={displayProperties} enableQuickIssueCreate={enableQuickIssueCreate} isReadonly={isReadonly} showEmptyGroup={showEmptyGroup} diff --git a/web/components/issues/issue-layouts/list/headers/group-by-card.tsx b/web/components/issues/issue-layouts/list/headers/group-by-card.tsx index 7397ae3d7..4a43fdf77 100644 --- a/web/components/issues/issue-layouts/list/headers/group-by-card.tsx +++ b/web/components/issues/issue-layouts/list/headers/group-by-card.tsx @@ -91,12 +91,12 @@ export const HeaderGroupByCard = observer(({ icon, title, count, issuePayload }: /> )}
-
- {icon ? icon : } +
+ {icon ? icon : }
@@ -114,8 +114,8 @@ export const HeaderGroupByCard = observer(({ icon, title, count, issuePayload }: - + + } > @@ -128,7 +128,7 @@ export const HeaderGroupByCard = observer(({ icon, title, count, issuePayload }: ) : (
setIsOpen(true)} > diff --git a/web/components/issues/issue-layouts/list/properties.tsx b/web/components/issues/issue-layouts/list/properties.tsx index c304867ac..92a203f36 100644 --- a/web/components/issues/issue-layouts/list/properties.tsx +++ b/web/components/issues/issue-layouts/list/properties.tsx @@ -11,19 +11,19 @@ import { IssuePropertyDate } from "../properties/date"; // ui import { Tooltip } from "@plane/ui"; // types -import { IIssue, IState, TIssuePriorities } from "types"; +import { IIssue, IIssueDisplayProperties, IState, TIssuePriorities } from "types"; export interface IKanBanProperties { columnId: string; issue: IIssue; handleIssues: (group_by: string | null, issue: IIssue) => void; - display_properties: any; + displayProperties: IIssueDisplayProperties; isReadonly?: boolean; showEmptyGroup?: boolean; } export const KanBanProperties: FC = observer((props) => { - const { columnId: group_id, issue, handleIssues, display_properties, isReadonly, showEmptyGroup } = props; + const { columnId: group_id, issue, handleIssues, displayProperties, isReadonly, showEmptyGroup } = props; const handleState = (state: IState) => { handleIssues(!group_id && group_id === "null" ? null : group_id, { ...issue, state: state.id }); @@ -57,7 +57,7 @@ export const KanBanProperties: FC = observer((props) => {
{/* basic properties */} {/* state */} - {display_properties && display_properties?.state && ( + {displayProperties && displayProperties?.state && ( = observer((props) => { )} {/* priority */} - {display_properties && display_properties?.priority && ( + {displayProperties && displayProperties?.priority && ( = observer((props) => { )} {/* label */} - {display_properties && display_properties?.labels && (showEmptyGroup || issue?.labels.length > 0) && ( + {displayProperties && displayProperties?.labels && (showEmptyGroup || issue?.labels.length > 0) && ( = observer((props) => { )} {/* assignee */} - {display_properties && display_properties?.assignee && (showEmptyGroup || issue?.assignees?.length > 0) && ( + {displayProperties && displayProperties?.assignee && (showEmptyGroup || issue?.assignees?.length > 0) && ( = observer((props) => { )} {/* start date */} - {display_properties && display_properties?.start_date && (showEmptyGroup || issue?.start_date) && ( + {displayProperties && displayProperties?.start_date && (showEmptyGroup || issue?.start_date) && ( handleStartDate(date)} @@ -111,7 +111,7 @@ export const KanBanProperties: FC = observer((props) => { )} {/* target/due date */} - {display_properties && display_properties?.due_date && (showEmptyGroup || issue?.target_date) && ( + {displayProperties && displayProperties?.due_date && (showEmptyGroup || issue?.target_date) && ( handleTargetDate(date)} @@ -121,7 +121,7 @@ export const KanBanProperties: FC = observer((props) => { )} {/* estimates */} - {display_properties && display_properties?.estimate && ( + {displayProperties && displayProperties?.estimate && ( = observer((props) => { {/* extra render properties */} {/* sub-issues */} - {display_properties && display_properties?.sub_issue_count && ( + {displayProperties && displayProperties?.sub_issue_count && (
@@ -143,7 +143,7 @@ export const KanBanProperties: FC = observer((props) => { )} {/* attachments */} - {display_properties && display_properties?.attachment_count && ( + {displayProperties && displayProperties?.attachment_count && (
@@ -153,7 +153,7 @@ export const KanBanProperties: FC = observer((props) => { )} {/* link */} - {display_properties && display_properties?.link && ( + {displayProperties && displayProperties?.link && (
diff --git a/web/components/issues/issue-layouts/list/roots/archived-issue-root.tsx b/web/components/issues/issue-layouts/list/roots/archived-issue-root.tsx index 63c2f1281..1ddbd5392 100644 --- a/web/components/issues/issue-layouts/list/roots/archived-issue-root.tsx +++ b/web/components/issues/issue-layouts/list/roots/archived-issue-root.tsx @@ -25,7 +25,7 @@ export const ArchivedIssueListLayout: FC = observer(() => { // derived values const issues = archivedIssueStore.getIssues; - const display_properties = archivedIssueFiltersStore?.userDisplayProperties || null; + const displayProperties = archivedIssueFiltersStore?.userDisplayProperties || null; const group_by: string | null = archivedIssueFiltersStore?.userDisplayFilters?.group_by || null; const handleIssues = (group_by: string | null, issue: IIssue, action: "delete" | "update") => { @@ -59,7 +59,7 @@ export const ArchivedIssueListLayout: FC = observer(() => { quickActions={(group_by, issue) => ( handleIssues(group_by, issue, "delete")} /> )} - display_properties={display_properties} + displayProperties={displayProperties} states={states} stateGroups={stateGroups} priorities={priorities} diff --git a/web/components/issues/issue-layouts/list/roots/cycle-root.tsx b/web/components/issues/issue-layouts/list/roots/cycle-root.tsx index 9a67b91c1..7bbb37f46 100644 --- a/web/components/issues/issue-layouts/list/roots/cycle-root.tsx +++ b/web/components/issues/issue-layouts/list/roots/cycle-root.tsx @@ -31,7 +31,7 @@ export const CycleListLayout: React.FC = observer(() => { const group_by: string | null = issueFilterStore?.userDisplayFilters?.group_by || null; - const display_properties = issueFilterStore?.userDisplayProperties || null; + const displayProperties = issueFilterStore?.userDisplayProperties || null; const handleIssues = useCallback( (group_by: string | null, issue: IIssue, action: "update" | "delete" | "remove") => { @@ -80,7 +80,7 @@ export const CycleListLayout: React.FC = observer(() => { handleRemoveFromCycle={async () => handleIssues(group_by, issue, "remove")} /> )} - display_properties={display_properties} + displayProperties={displayProperties} states={states} stateGroups={stateGroups} priorities={priorities} diff --git a/web/components/issues/issue-layouts/list/roots/module-root.tsx b/web/components/issues/issue-layouts/list/roots/module-root.tsx index fd748bc6c..11704f3b6 100644 --- a/web/components/issues/issue-layouts/list/roots/module-root.tsx +++ b/web/components/issues/issue-layouts/list/roots/module-root.tsx @@ -31,7 +31,7 @@ export const ModuleListLayout: React.FC = observer(() => { const group_by: string | null = issueFilterStore?.userDisplayFilters?.group_by || null; - const display_properties = issueFilterStore?.userDisplayProperties || null; + const displayProperties = issueFilterStore?.userDisplayProperties || null; const handleIssues = useCallback( (group_by: string | null, issue: IIssue, action: "update" | "delete" | "remove") => { @@ -80,7 +80,7 @@ export const ModuleListLayout: React.FC = observer(() => { handleRemoveFromModule={async () => handleIssues(group_by, issue, "remove")} /> )} - display_properties={display_properties} + displayProperties={displayProperties} states={states} stateGroups={stateGroups} priorities={priorities} diff --git a/web/components/issues/issue-layouts/list/roots/profile-issues-root.tsx b/web/components/issues/issue-layouts/list/roots/profile-issues-root.tsx index 9e4937ffd..e66cda2d2 100644 --- a/web/components/issues/issue-layouts/list/roots/profile-issues-root.tsx +++ b/web/components/issues/issue-layouts/list/roots/profile-issues-root.tsx @@ -29,7 +29,7 @@ export const ProfileIssuesListLayout: FC = observer(() => { const group_by: string | null = profileIssueFiltersStore?.userDisplayFilters?.group_by || null; - const display_properties = profileIssueFiltersStore?.userDisplayProperties || null; + const displayProperties = profileIssueFiltersStore?.userDisplayProperties || null; const handleIssues = useCallback( (group_by: string | null, issue: IIssue, action: "update" | "delete") => { @@ -64,7 +64,7 @@ export const ProfileIssuesListLayout: FC = observer(() => { handleUpdate={async (data) => handleIssues(group_by, data, "update")} /> )} - display_properties={display_properties} + displayProperties={displayProperties} states={states} stateGroups={stateGroups} priorities={priorities} diff --git a/web/components/issues/issue-layouts/list/roots/project-root.tsx b/web/components/issues/issue-layouts/list/roots/project-root.tsx index 16ce940b5..8ed96d54c 100644 --- a/web/components/issues/issue-layouts/list/roots/project-root.tsx +++ b/web/components/issues/issue-layouts/list/roots/project-root.tsx @@ -6,6 +6,7 @@ import { useMobxStore } from "lib/mobx/store-provider"; // components import { List } from "../default"; import { ProjectIssueQuickActions } from "components/issues"; +import { Spinner } from "@plane/ui"; // helpers import { orderArrayBy } from "helpers/array.helper"; // types @@ -29,7 +30,7 @@ export const ListLayout: FC = observer(() => { const userDisplayFilters = issueFilterStore?.userDisplayFilters || null; const group_by: string | null = userDisplayFilters?.group_by || null; - const display_properties = issueFilterStore?.userDisplayProperties || null; + const displayProperties = issueFilterStore?.userDisplayProperties || null; const handleIssues = useCallback( (group_by: string | null, issue: IIssue, action: "update" | "delete") => { @@ -56,29 +57,37 @@ export const ListLayout: FC = observer(() => { : null; return ( -
- ( - handleIssues(group_by, issue, "delete")} - handleUpdate={async (data) => handleIssues(group_by, data, "update")} + <> + {issueStore.loader ? ( +
+ +
+ ) : ( +
+ ( + handleIssues(group_by, issue, "delete")} + handleUpdate={async (data) => handleIssues(group_by, data, "update")} + /> + )} + displayProperties={displayProperties} + states={states} + stateGroups={stateGroups} + priorities={priorities} + labels={labels} + members={members?.map((m) => m.member) ?? null} + projects={projects} + enableQuickIssueCreate + estimates={estimates?.points ? orderArrayBy(estimates.points, "key") : null} + showEmptyGroup={userDisplayFilters.show_empty_groups} /> - )} - display_properties={display_properties} - states={states} - stateGroups={stateGroups} - priorities={priorities} - labels={labels} - members={members?.map((m) => m.member) ?? null} - projects={projects} - enableQuickIssueCreate - estimates={estimates?.points ? orderArrayBy(estimates.points, "key") : null} - showEmptyGroup={userDisplayFilters.show_empty_groups} - /> -
+
+ )} + ); }); diff --git a/web/components/issues/issue-layouts/properties/estimates.tsx b/web/components/issues/issue-layouts/properties/estimates.tsx index 907946ca7..573738975 100644 --- a/web/components/issues/issue-layouts/properties/estimates.tsx +++ b/web/components/issues/issue-layouts/properties/estimates.tsx @@ -1,16 +1,13 @@ import { Fragment, useState } from "react"; - -import { observer } from "mobx-react-lite"; - -// hooks import { usePopper } from "react-popper"; -import useEstimateOption from "hooks/use-estimate-option"; -// ui -import { Check, ChevronDown, Search, Triangle } from "lucide-react"; +import { observer } from "mobx-react-lite"; import { Combobox } from "@headlessui/react"; +import { Check, ChevronDown, Search, Triangle } from "lucide-react"; +// ui import { Tooltip } from "@plane/ui"; // types import { Placement } from "@popperjs/core"; +import { useMobxStore } from "lib/mobx/store-provider"; export interface IIssuePropertyEstimates { view?: "profile" | "workspace" | "project"; @@ -27,7 +24,6 @@ export interface IIssuePropertyEstimates { export const IssuePropertyEstimates: React.FC = observer((props) => { const { - view, projectId, value, onChange, @@ -44,8 +40,6 @@ export const IssuePropertyEstimates: React.FC = observe const [referenceElement, setReferenceElement] = useState(null); const [popperElement, setPopperElement] = useState(null); - const { isEstimateActive, estimatePoints } = useEstimateOption(); - const { styles, attributes } = usePopper(referenceElement, popperElement, { placement: placement ?? "bottom-start", modifiers: [ @@ -58,6 +52,14 @@ export const IssuePropertyEstimates: React.FC = observe ], }); + const { project: projectStore } = useMobxStore(); + + const projectDetails = projectId ? projectStore.project_details[projectId] : null; + const isEstimateEnabled = projectDetails?.estimate !== null; + const estimates = projectId ? projectStore.estimates?.[projectId] : null; + const estimatePoints = + projectDetails && isEstimateEnabled ? estimates?.find((e) => e.id === projectDetails.estimate)?.points : null; + const options: { value: number | null; query: string; content: any }[] | undefined = (estimatePoints ?? []).map( (estimate) => ({ value: estimate.key, @@ -94,6 +96,8 @@ export const IssuePropertyEstimates: React.FC = observe ); + if (!isEstimateEnabled) return null; + return ( void; -}; - -export const CycleLayoutRoot: React.FC = observer(({ openIssuesListModal }) => { +export const CycleLayoutRoot: React.FC = observer(() => { const [transferIssuesModal, setTransferIssuesModal] = useState(false); const router = useRouter(); @@ -73,7 +69,11 @@ export const CycleLayoutRoot: React.FC = observer(({ openIssuesListModal {cycleStatus === "completed" && setTransferIssuesModal(true)} />} {(activeLayout === "list" || activeLayout === "spreadsheet") && issueCount === 0 ? ( - + ) : (
{activeLayout === "list" ? ( diff --git a/web/components/issues/issue-layouts/roots/module-layout-root.tsx b/web/components/issues/issue-layouts/roots/module-layout-root.tsx index d4f1efb28..3386725bc 100644 --- a/web/components/issues/issue-layouts/roots/module-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/module-layout-root.tsx @@ -18,11 +18,7 @@ import { // ui import { Spinner } from "@plane/ui"; -type Props = { - openIssuesListModal: () => void; -}; - -export const ModuleLayoutRoot: React.FC = observer(({ openIssuesListModal }) => { +export const ModuleLayoutRoot: React.FC = observer(() => { const router = useRouter(); const { workspaceSlug, projectId, moduleId } = router.query as { workspaceSlug: string; @@ -66,7 +62,11 @@ export const ModuleLayoutRoot: React.FC = observer(({ openIssuesListModal
{(activeLayout === "list" || activeLayout === "spreadsheet") && issueCount === 0 ? ( - + ) : (
{activeLayout === "list" ? ( diff --git a/web/components/issues/issue-layouts/roots/project-layout-root.tsx b/web/components/issues/issue-layouts/roots/project-layout-root.tsx index 6990c43ef..0f5cac86b 100644 --- a/web/components/issues/issue-layouts/roots/project-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/project-layout-root.tsx @@ -22,13 +22,19 @@ export const ProjectLayoutRoot: React.FC = observer(() => { const { issue: issueStore, issueFilter: issueFilterStore } = useMobxStore(); - useSWR(workspaceSlug && projectId ? `PROJECT_FILTERS_AND_ISSUES_${projectId.toString()}` : null, async () => { - if (workspaceSlug && projectId) { - await issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString()); - - await issueStore.fetchIssues(workspaceSlug.toString(), projectId.toString()); + const { isLoading } = useSWR( + workspaceSlug && projectId ? `PROJECT_FILTERS_AND_ISSUES_${projectId.toString()}` : null, + async () => { + if (workspaceSlug && projectId) { + await issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString()); + await issueStore.fetchIssues(workspaceSlug.toString(), projectId.toString()); + } } - }); + ); + + console.log("--"); + console.log("isLoading -- -->", isLoading); + console.log("--"); const activeLayout = issueFilterStore.userDisplayFilters.layout; diff --git a/web/components/issues/issue-peek-overview/activity/comment-card.tsx b/web/components/issues/issue-peek-overview/activity/comment-card.tsx index fc8fbf179..4ca072f6e 100644 --- a/web/components/issues/issue-peek-overview/activity/comment-card.tsx +++ b/web/components/issues/issue-peek-overview/activity/comment-card.tsx @@ -49,7 +49,7 @@ export const IssueCommentCard: React.FC = (props) => { const [isEditing, setIsEditing] = useState(false); - const editorSuggestions = useEditorSuggestions(workspaceSlug, projectId); + const editorSuggestions = useEditorSuggestions(); const { formState: { isSubmitting }, diff --git a/web/components/issues/issue-peek-overview/activity/comment-editor.tsx b/web/components/issues/issue-peek-overview/activity/comment-editor.tsx index eea88e201..1eccfb33e 100644 --- a/web/components/issues/issue-peek-overview/activity/comment-editor.tsx +++ b/web/components/issues/issue-peek-overview/activity/comment-editor.tsx @@ -51,9 +51,9 @@ export const IssueCommentEditor: React.FC = (props) => { const editorRef = React.useRef(null); const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const { workspaceSlug } = router.query; - const editorSuggestions = useEditorSuggestions(workspaceSlug as string | undefined, projectId as string | undefined); + const editorSuggestions = useEditorSuggestions(); const { control, @@ -90,6 +90,7 @@ export const IssueCommentEditor: React.FC = (props) => { ref={editorRef} value={!commentValue || commentValue === "" ? "

" : commentValue} customClassName="p-2 h-full" + editorContentCustomClassNames="min-h-[35px]" debouncedUpdatesEnabled={false} mentionSuggestions={editorSuggestions.mentionSuggestions} mentionHighlights={editorSuggestions.mentionHighlights} diff --git a/web/components/issues/issue-peek-overview/issue-detail.tsx b/web/components/issues/issue-peek-overview/issue-detail.tsx index 780186b50..2aee4aedd 100644 --- a/web/components/issues/issue-peek-overview/issue-detail.tsx +++ b/web/components/issues/issue-peek-overview/issue-detail.tsx @@ -32,13 +32,13 @@ export const PeekOverviewIssueDetails: FC = (props) = // store const { user: userStore } = useMobxStore(); const { currentProjectRole } = userStore; - const isAllowed = [5, 10].includes(currentProjectRole || 0); + const isAllowed = [15, 20].includes(currentProjectRole || 0); // states const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved"); const [characterLimit, setCharacterLimit] = useState(false); // hooks const { setShowAlert } = useReloadConfirmations(); - const editorSuggestions = useEditorSuggestions(workspaceSlug, issue.project_detail.id); + const editorSuggestions = useEditorSuggestions(); const { handleSubmit, diff --git a/web/components/issues/issue-peek-overview/properties.tsx b/web/components/issues/issue-peek-overview/properties.tsx index dbc1bfdbb..303687d39 100644 --- a/web/components/issues/issue-peek-overview/properties.tsx +++ b/web/components/issues/issue-peek-overview/properties.tsx @@ -74,13 +74,13 @@ export const PeekOverviewProperties: FC = observer((pro }; const addIssueToCycle = async (cycleId: string) => { if (!workspaceSlug || !issue || !cycleId) return; - cycleIssueStore.addIssueToCycle(workspaceSlug.toString(), issue.project_detail.id, cycleId, issue.id); + cycleIssueStore.addIssueToCycle(workspaceSlug.toString(), issue.project_detail.id, cycleId, [issue.id]); }; const addIssueToModule = async (moduleId: string) => { if (!workspaceSlug || !issue || !moduleId) return; - moduleIssueStore.addIssueToModule(workspaceSlug.toString(), issue.project_detail.id, moduleId, issue.id); + moduleIssueStore.addIssueToModule(workspaceSlug.toString(), issue.project_detail.id, moduleId, [issue.id]); }; const handleLabels = (formData: Partial) => { issueUpdate({ ...issue, ...formData }); diff --git a/web/components/issues/issue-peek-overview/root.tsx b/web/components/issues/issue-peek-overview/root.tsx index 255e22929..6c8631b5b 100644 --- a/web/components/issues/issue-peek-overview/root.tsx +++ b/web/components/issues/issue-peek-overview/root.tsx @@ -1,4 +1,4 @@ -import { FC, ReactNode } from "react"; +import { FC, Fragment, ReactNode } from "react"; import { useRouter } from "next/router"; import useSWR from "swr"; import { observer } from "mobx-react-lite"; @@ -112,6 +112,7 @@ export const IssuePeekOverview: FC = observer((props) => { else await issueStore.deleteIssue(workspaceSlug, projectId, issue!); const { query } = router; if (query.peekIssueId) { + issueDetailStore.setPeekId(null); delete query.peekIssueId; router.push({ pathname: router.pathname, @@ -123,30 +124,32 @@ export const IssuePeekOverview: FC = observer((props) => { const userRole = userStore.currentProjectRole ?? 5; return ( - - {children} - + + + {children} + + ); }); diff --git a/web/components/issues/issue-peek-overview/view.tsx b/web/components/issues/issue-peek-overview/view.tsx index 509fe593a..3342cbf40 100644 --- a/web/components/issues/issue-peek-overview/view.tsx +++ b/web/components/issues/issue-peek-overview/view.tsx @@ -97,6 +97,7 @@ export const IssueView: FC = observer((props) => { const updateRoutePeekId = () => { if (issueId != peekIssueId) { + issueDetailStore.setPeekId(issueId); const { query } = router; router.push({ pathname: router.pathname, @@ -107,6 +108,7 @@ export const IssueView: FC = observer((props) => { const removeRoutePeekId = () => { const { query } = router; if (query.peekIssueId) { + issueDetailStore.setPeekId(null); delete query.peekIssueId; router.push({ pathname: router.pathname, @@ -248,7 +250,7 @@ export const IssueView: FC = observer((props) => { issue && ( <> {["side-peek", "modal"].includes(peekMode) ? ( -
+
= observer((props) => { issueReactionRemove={issueReactionRemove} /> -
- = observer((prop const addIssueToCycle = async (issueId: string, cycleId: string) => { if (!workspaceSlug || !activeProject) return; - cycleIssueStore.addIssueToCycle(workspaceSlug.toString(), activeProject, cycleId, issueId); + cycleIssueStore.addIssueToCycle(workspaceSlug.toString(), activeProject, cycleId, [issueId]); }; const addIssueToModule = async (issueId: string, moduleId: string) => { if (!workspaceSlug || !activeProject) return; - moduleIssueStore.addIssueToModule(workspaceSlug.toString(), activeProject, moduleId, issueId); + moduleIssueStore.addIssueToModule(workspaceSlug.toString(), activeProject, moduleId, [issueId]); }; const createIssue = async (payload: Partial) => { @@ -320,7 +320,7 @@ export const CreateUpdateIssueModal: React.FC = observer((prop leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - + { - const store: any = useMobxStore(); + const { theme: themeStore } = useMobxStore(); const { notifications, @@ -45,6 +45,8 @@ export const NotificationPopover = observer(() => { markAllNotificationsAsRead, } = useUserNotification(); + const isSidebarCollapsed = themeStore.sidebarCollapsed; + return ( <> { return ( <> - + - {store?.theme?.sidebarCollapsed ? null : Notifications} + {isSidebarCollapsed ? null : Notifications} {totalNotificationCount && totalNotificationCount > 0 ? ( - store?.theme?.sidebarCollapsed ? ( + isSidebarCollapsed ? ( ) : ( diff --git a/web/components/onboarding/tour/root.tsx b/web/components/onboarding/tour/root.tsx index 9b3384f44..713c1140e 100644 --- a/web/components/onboarding/tour/root.tsx +++ b/web/components/onboarding/tour/root.tsx @@ -1,16 +1,15 @@ import { useState } from "react"; - import Image from "next/image"; - -// hooks -import useUser from "hooks/use-user"; +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; // components import { TourSidebar } from "components/onboarding"; // ui import { Button } from "@plane/ui"; // icons import { X } from "lucide-react"; -// images +// assets import PlaneWhiteLogo from "public/plane-logos/white-horizontal.svg"; import IssuesTour from "public/onboarding/issues.webp"; import CyclesTour from "public/onboarding/cycles.webp"; @@ -75,10 +74,13 @@ const TOUR_STEPS: { }, ]; -export const TourRoot: React.FC = ({ onComplete }) => { +export const TourRoot: React.FC = observer((props) => { + const { onComplete } = props; + // states const [step, setStep] = useState("welcome"); - const { user } = useUser(); + const { user: userStore, commandPalette: commandPaletteStore } = useMobxStore(); + const user = userStore.currentUser; const currentStepIndex = TOUR_STEPS.findIndex((tourStep) => tourStep.key === step); const currentStep = TOUR_STEPS[currentStepIndex]; @@ -91,7 +93,7 @@ export const TourRoot: React.FC = ({ onComplete }) => {
Plane White Logo
-
+

Welcome to Plane, {user?.first_name} {user?.last_name}

@@ -99,17 +101,19 @@ export const TourRoot: React.FC = ({ onComplete }) => { We{"'"}re glad that you decided to try out Plane. You can now manage your projects with ease. Get started by creating a project.

-
- - +
+
+ + +
@@ -148,8 +152,14 @@ export const TourRoot: React.FC = ({ onComplete }) => { )}
- {TOUR_STEPS.findIndex((tourStep) => tourStep.key === step) === TOUR_STEPS.length - 1 && ( - )} @@ -160,4 +170,4 @@ export const TourRoot: React.FC = ({ onComplete }) => { )} ); -}; +}); diff --git a/web/components/page-views/index.ts b/web/components/page-views/index.ts index 717070c97..93928bede 100644 --- a/web/components/page-views/index.ts +++ b/web/components/page-views/index.ts @@ -1 +1,2 @@ export * from "./signin"; +export * from "./workspace-dashboard"; diff --git a/web/components/views/workspace-dashboard.tsx b/web/components/page-views/workspace-dashboard.tsx similarity index 100% rename from web/components/views/workspace-dashboard.tsx rename to web/components/page-views/workspace-dashboard.tsx diff --git a/web/components/pages/create-update-block-inline.tsx b/web/components/pages/create-update-block-inline.tsx index 1d0da5018..b838da0c9 100644 --- a/web/components/pages/create-update-block-inline.tsx +++ b/web/components/pages/create-update-block-inline.tsx @@ -56,7 +56,7 @@ export const CreateUpdateBlockInline: FC = ({ const router = useRouter(); const { workspaceSlug, projectId, pageId } = router.query; - const editorSuggestion = useEditorSuggestions(workspaceSlug as string | undefined, projectId as string | undefined) + const editorSuggestion = useEditorSuggestions(); const { setToastAlert } = useToast(); diff --git a/web/components/pages/single-page-block.tsx b/web/components/pages/single-page-block.tsx index 32092c036..542ef78a2 100644 --- a/web/components/pages/single-page-block.tsx +++ b/web/components/pages/single-page-block.tsx @@ -3,7 +3,7 @@ import { useRouter } from "next/router"; import Link from "next/link"; import { mutate } from "swr"; import { useForm } from "react-hook-form"; -import { Draggable } from "react-beautiful-dnd"; +import { Draggable } from "@hello-pangea/dnd"; // services import { PageService } from "services/page.service"; import { IssueService } from "services/issue/issue.service"; @@ -64,7 +64,7 @@ export const SinglePageBlock: React.FC = ({ block, projectDetails, showBl }, }); - const editorSuggestion = useEditorSuggestions(workspaceSlug as string | undefined, projectId as string | undefined) + const editorSuggestion = useEditorSuggestions(); const updatePageBlock = async (formData: Partial) => { if (!workspaceSlug || !projectId || !pageId) return; diff --git a/web/components/project/card.tsx b/web/components/project/card.tsx index 3993a74e9..533e94ede 100644 --- a/web/components/project/card.tsx +++ b/web/components/project/card.tsx @@ -211,7 +211,7 @@ export const ProjectCard: React.FC = observer((props) => {
-
+
= observer((props) => { )} />
-
+
= observer((props) => {
- -
diff --git a/web/components/project/sidebar-list-item.tsx b/web/components/project/sidebar-list-item.tsx index 58a006fd0..44c248114 100644 --- a/web/components/project/sidebar-list-item.tsx +++ b/web/components/project/sidebar-list-item.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import Link from "next/link"; import { useRouter } from "next/router"; -import { DraggableProvided, DraggableStateSnapshot } from "react-beautiful-dnd"; +import { DraggableProvided, DraggableStateSnapshot } from "@hello-pangea/dnd"; import { Disclosure, Transition } from "@headlessui/react"; import { observer } from "mobx-react-lite"; // icons diff --git a/web/components/project/sidebar-list.tsx b/web/components/project/sidebar-list.tsx index 70a7aca8a..98d3cdb02 100644 --- a/web/components/project/sidebar-list.tsx +++ b/web/components/project/sidebar-list.tsx @@ -1,6 +1,6 @@ import React, { useState, FC, useRef, useEffect } from "react"; import { useRouter } from "next/router"; -import { DragDropContext, Draggable, DropResult, Droppable } from "react-beautiful-dnd"; +import { DragDropContext, Draggable, DropResult, Droppable } from "@hello-pangea/dnd"; import { Disclosure, Transition } from "@headlessui/react"; import { observer } from "mobx-react-lite"; // hooks diff --git a/web/components/ui/icon-name-type.d.ts b/web/components/ui/icon-name-type.d.ts deleted file mode 100644 index 0d886b08e..000000000 --- a/web/components/ui/icon-name-type.d.ts +++ /dev/null @@ -1,2991 +0,0 @@ -type IconName = - | "search" - | "home" - | "menu" - | "close" - | "settings" - | "done" - | "expand_more" - | "check_circle" - | "favorite" - | "add" - | "delete" - | "arrow_back" - | "star" - | "chevron_right" - | "logout" - | "arrow_forward_ios" - | "add_circle" - | "cancel" - | "arrow_back_ios" - | "arrow_forward" - | "arrow_drop_down" - | "more_vert" - | "check" - | "check_box" - | "toggle_on" - | "grade" - | "open_in_new" - | "check_box_outline_blank" - | "refresh" - | "login" - | "chevron_left" - | "expand_less" - | "radio_button_unchecked" - | "more_horiz" - | "apps" - | "arrow_right_alt" - | "radio_button_checked" - | "download" - | "remove" - | "bolt" - | "toggle_off" - | "arrow_upward" - | "filter_list" - | "delete_forever" - | "autorenew" - | "key" - | "arrow_downward" - | "sync" - | "sort" - | "block" - | "add_box" - | "arrow_back_ios_new" - | "restart_alt" - | "menu_open" - | "shopping_cart_checkout" - | "expand_circle_down" - | "backspace" - | "undo" - | "arrow_circle_right" - | "done_all" - | "arrow_right" - | "do_not_disturb_on" - | "open_in_full" - | "manage_search" - | "double_arrow" - | "sync_alt" - | "zoom_in" - | "done_outline" - | "drag_indicator" - | "fullscreen" - | "keyboard_double_arrow_right" - | "star_half" - | "settings_accessibility" - | "ios_share" - | "arrow_drop_up" - | "reply" - | "exit_to_app" - | "unfold_more" - | "library_add" - | "cached" - | "select_check_box" - | "terminal" - | "change_circle" - | "disabled_by_default" - | "swap_horiz" - | "swap_vert" - | "close_fullscreen" - | "app_registration" - | "download_for_offline" - | "arrow_circle_left" - | "arrow_circle_up" - | "file_open" - | "minimize" - | "open_with" - | "keyboard_double_arrow_left" - | "dataset" - | "add_task" - | "start" - | "keyboard_double_arrow_down" - | "keyboard_voice" - | "create_new_folder" - | "forward" - | "downloading" - | "settings_applications" - | "compare_arrows" - | "redo" - | "publish" - | "arrow_left" - | "zoom_out" - | "html" - | "token" - | "switch_access_shortcut" - | "arrow_circle_down" - | "fullscreen_exit" - | "sort_by_alpha" - | "delete_sweep" - | "indeterminate_check_box" - | "first_page" - | "keyboard_double_arrow_up" - | "view_timeline" - | "arrow_drop_down_circle" - | "assistant_navigation" - | "settings_backup_restore" - | "sync_problem" - | "clear_all" - | "density_medium" - | "heart_plus" - | "filter_alt_off" - | "expand" - | "last_page" - | "subdirectory_arrow_right" - | "unfold_less" - | "arrow_outward" - | "download_done" - | "123" - | "swipe_left" - | "saved_search" - | "system_update_alt" - | "place_item" - | "maximize" - | "javascript" - | "output" - | "search_off" - | "swipe_up" - | "fit_screen" - | "select_all" - | "dynamic_form" - | "hide_source" - | "swipe_right" - | "switch_access_shortcut_add" - | "browse_gallery" - | "check_small" - | "css" - | "density_small" - | "assistant_direction" - | "youtube_searched_for" - | "file_download_done" - | "move_up" - | "swap_horizontal_circle" - | "data_thresholding" - | "install_mobile" - | "move_down" - | "abc" - | "dataset_linked" - | "restore_from_trash" - | "enable" - | "install_desktop" - | "keyboard_command_key" - | "view_kanban" - | "browse_activity" - | "reply_all" - | "switch_left" - | "compress" - | "swipe_down" - | "swap_vertical_circle" - | "remove_done" - | "apps_outage" - | "filter_list_off" - | "star_rate" - | "hide" - | "switch_right" - | "swipe_vertical" - | "more_up" - | "sync_disabled" - | "pinch" - | "keyboard_control_key" - | "eject" - | "key_off" - | "php" - | "subdirectory_arrow_left" - | "view_cozy" - | "transcribe" - | "do_not_disturb_off" - | "send_time_extension" - | "width_normal" - | "heart_minus" - | "view_comfy_alt" - | "share_reviews" - | "width_full" - | "unfold_more_double" - | "view_compact_alt" - | "file_download_off" - | "extension_off" - | "open_in_new_off" - | "check_indeterminate_small" - | "more_down" - | "width_wide" - | "repartition" - | "density_large" - | "swipe_left_alt" - | "swipe_down_alt" - | "swipe_right_alt" - | "swipe_up_alt" - | "unfold_less_double" - | "keyboard_option_key" - | "cycle" - | "hls" - | "hls_off" - | "progress_activity" - | "file_upload_off" - | "rebase" - | "expand_content" - | "expand_all" - | "rebase_edit" - | "collapse_all" - | "empty_dashboard" - | "arrow_split" - | "quick_reference_all" - | "arrow_upward_alt" - | "switches" - | "arrow_downward_alt" - | "directory_sync" - | "quick_reference" - | "cards" - | "deployed_code" - | "side_navigation" - | "data_check" - | "acute" - | "bubble" - | "left_click" - | "sync_saved_locally" - | "data_alert" - | "move_item" - | "steppers" - | "stack" - | "stat_3" - | "arrow_left_alt" - | "clock_loader_60" - | "page_info" - | "data_info_alert" - | "search_check" - | "question_exchange" - | "point_scan" - | "preliminary" - | "toolbar" - | "clock_loader_10" - | "sweep" - | "new_window" - | "right_panel_close" - | "captive_portal" - | "star_rate_half" - | "page_control" - | "patient_list" - | "right_panel_open" - | "stat_2" - | "stat_minus_1" - | "unknown_med" - | "stat_minus_2" - | "clock_loader_40" - | "dialogs" - | "capture" - | "step_into" - | "arrow_insert" - | "heart_check" - | "magnification_large" - | "partner_reports" - | "stack_star" - | "drag_pan" - | "magnification_small" - | "stat_1" - | "clock_loader_90" - | "dropdown" - | "left_panel_close" - | "left_panel_open" - | "sliders" - | "bottom_panel_open" - | "clock_loader_80" - | "move_group" - | "bottom_navigation" - | "stack_off" - | "all_match" - | "clock_loader_20" - | "step" - | "stat_minus_3" - | "bottom_drawer" - | "buttons_alt" - | "chip_extraction" - | "bottom_sheets" - | "chronic" - | "pip" - | "resize" - | "tabs" - | "chips" - | "iframe" - | "input_circle" - | "reopen_window" - | "ripples" - | "rule_settings" - | "toast" - | "unknown_5" - | "share_windows" - | "shelf_position" - | "go_to_line" - | "right_click" - | "subheader" - | "bottom_right_click" - | "drag_click" - | "expand_circle_up" - | "step_out" - | "step_over" - | "switch_access" - | "arrow_and_edge" - | "error_med" - | "app_badging" - | "deployed_code_alert" - | "event_list" - | "move_selection_left" - | "move_selection_right" - | "output_circle" - | "amend" - | "pip_exit" - | "arrow_or_edge" - | "arrow_top_right" - | "bottom_app_bar" - | "deployed_code_update" - | "iframe_off" - | "shelf_auto_hide" - | "arrow_range" - | "bottom_panel_close" - | "bubbles" - | "position_bottom_right" - | "arrow_top_left" - | "arrows_outward" - | "back_to_tab" - | "jump_to_element" - | "move_selection_down" - | "move_selection_up" - | "open_in_new_down" - | "deployed_code_history" - | "position_bottom_left" - | "position_top_right" - | "expand_circle_right" - | "person" - | "group" - | "share" - | "thumb_up" - | "groups" - | "person_add" - | "public" - | "handshake" - | "support_agent" - | "face" - | "sentiment_satisfied" - | "rocket_launch" - | "group_add" - | "workspace_premium" - | "psychology" - | "diversity_3" - | "emoji_objects" - | "water_drop" - | "eco" - | "pets" - | "travel_explore" - | "mood" - | "sunny" - | "quiz" - | "health_and_safety" - | "sentiment_dissatisfied" - | "sentiment_very_satisfied" - | "military_tech" - | "thumb_down" - | "gavel" - | "recycling" - | "diamond" - | "monitor_heart" - | "emoji_people" - | "diversity_1" - | "workspaces" - | "vaccines" - | "compost" - | "forest" - | "recommend" - | "waving_hand" - | "person_remove" - | "wc" - | "medication" - | "sentiment_neutral" - | "group_work" - | "sentiment_very_dissatisfied" - | "diversity_2" - | "front_hand" - | "psychology_alt" - | "cruelty_free" - | "man" - | "medical_information" - | "coronavirus" - | "add_reaction" - | "rocket" - | "female" - | "potted_plant" - | "emoji_nature" - | "rainy" - | "person_off" - | "cookie" - | "woman" - | "connect_without_contact" - | "mood_bad" - | "male" - | "bedtime" - | "solar_power" - | "communication" - | "thunderstorm" - | "groups_2" - | "partly_cloudy_day" - | "thumbs_up_down" - | "emoji_flags" - | "masks" - | "hive" - | "heart_broken" - | "sentiment_extremely_dissatisfied" - | "clear_day" - | "boy" - | "whatshot" - | "cloudy_snowing" - | "emoji_food_beverage" - | "wind_power" - | "emoji_transportation" - | "elderly" - | "face_6" - | "reduce_capacity" - | "sick" - | "pregnant_woman" - | "face_3" - | "bloodtype" - | "group_remove" - | "egg" - | "medication_liquid" - | "groups_3" - | "co2" - | "clear_night" - | "weight" - | "skull" - | "follow_the_signs" - | "face_4" - | "emoji_events" - | "oil_barrel" - | "transgender" - | "elderly_woman" - | "clean_hands" - | "sanitizer" - | "person_2" - | "bring_your_own_ip" - | "public_off" - | "face_2" - | "social_distance" - | "routine" - | "sign_language" - | "south_america" - | "sunny_snowing" - | "emoji_symbols" - | "garden_cart" - | "flood" - | "face_5" - | "egg_alt" - | "cyclone" - | "girl" - | "person_4" - | "dentistry" - | "tsunami" - | "group_off" - | "outdoor_garden" - | "partly_cloudy_night" - | "severe_cold" - | "snowing" - | "person_3" - | "tornado" - | "landslide" - | "vaping_rooms" - | "safety_divider" - | "foggy" - | "woman_2" - | "no_adult_content" - | "volcano" - | "man_2" - | "blind" - | "18_up_rating" - | "6_ft_apart" - | "vape_free" - | "not_accessible" - | "man_4" - | "radiology" - | "rib_cage" - | "hand_bones" - | "bedtime_off" - | "rheumatology" - | "man_3" - | "orthopedics" - | "tibia" - | "skeleton" - | "partner_exchange" - | "humerus" - | "agender" - | "femur" - | "foot_bones" - | "tibia_alt" - | "femur_alt" - | "humerus_alt" - | "communities" - | "diversity_4" - | "ulna_radius" - | "ulna_radius_alt" - | "specific_gravity" - | "cognition" - | "breastfeeding" - | "eyeglasses" - | "psychiatry" - | "labs" - | "crowdsource" - | "footprint" - | "vital_signs" - | "nutrition" - | "neurology" - | "social_leaderboard" - | "demography" - | "globe_asia" - | "stethoscope" - | "conditions" - | "lab_research" - | "clinical_notes" - | "sentiment_excited" - | "sentiment_stressed" - | "taunt" - | "altitude" - | "glucose" - | "globe_uk" - | "mystery" - | "strategy" - | "home_health" - | "sentiment_calm" - | "crossword" - | "prayer_times" - | "recent_patient" - | "chess" - | "pill" - | "sentiment_sad" - | "share_off" - | "weather_hail" - | "cardiology" - | "falling" - | "helicopter" - | "mist" - | "prescriptions" - | "sentiment_content" - | "wrist" - | "deceased" - | "genetics" - | "weather_mix" - | "dew_point" - | "sentiment_frustrated" - | "cheer" - | "metabolism" - | "microbiology" - | "body_system" - | "earthquake" - | "ent" - | "explosion" - | "pulmonology" - | "stethoscope_check" - | "infrared" - | "oxygen_saturation" - | "person_raised_hand" - | "sentiment_worried" - | "sword_rose" - | "barefoot" - | "cookie_off" - | "domino_mask" - | "emoticon" - | "humidity_percentage" - | "playing_cards" - | "stethoscope_arrow" - | "water_lux" - | "comic_bubble" - | "lab_panel" - | "mountain_flag" - | "ophthalmology" - | "water_bottle" - | "water_do" - | "water_voc" - | "allergies" - | "allergy" - | "blood_pressure" - | "dermatology" - | "gynecology" - | "immunology" - | "manga" - | "oncology" - | "oral_disease" - | "person_apron" - | "short_stay" - | "water_orp" - | "water_ph" - | "body_fat" - | "endocrinology" - | "folded_hands" - | "hematology" - | "inpatient" - | "mixture_med" - | "moving_beds" - | "nephrology" - | "respiratory_rate" - | "symptoms" - | "ward" - | "congenital" - | "gastroenterology" - | "medical_mask" - | "outpatient" - | "outpatient_med" - | "pediatrics" - | "procedure" - | "rainy_snow" - | "salinity" - | "surgical" - | "syringe" - | "urology" - | "wounds_injuries" - | "admin_meds" - | "fluid" - | "fluid_balance" - | "fluid_med" - | "pill_off" - | "pregnancy" - | "total_dissolved_solids" - | "rainy_heavy" - | "rainy_light" - | "snowing_heavy" - | "water_bottle_large" - | "water_ec" - | "account_circle" - | "info" - | "visibility" - | "calendar_month" - | "schedule" - | "help" - | "language" - | "warning" - | "lock" - | "error" - | "visibility_off" - | "verified" - | "manage_accounts" - | "history" - | "task_alt" - | "event" - | "bookmark" - | "calendar_today" - | "question_mark" - | "lightbulb" - | "fingerprint" - | "category" - | "update" - | "lock_open" - | "priority_high" - | "code" - | "build" - | "date_range" - | "upload_file" - | "supervisor_account" - | "event_available" - | "ads_click" - | "today" - | "touch_app" - | "pending" - | "preview" - | "stars" - | "new_releases" - | "account_box" - | "celebration" - | "how_to_reg" - | "translate" - | "bug_report" - | "push_pin" - | "alarm" - | "edit_calendar" - | "edit_square" - | "label" - | "extension" - | "event_note" - | "record_voice_over" - | "rate_review" - | "web" - | "hourglass_empty" - | "published_with_changes" - | "support" - | "notification_important" - | "upload" - | "help_center" - | "accessibility_new" - | "bookmarks" - | "pan_tool_alt" - | "supervised_user_circle" - | "dangerous" - | "collections_bookmark" - | "interests" - | "all_inclusive" - | "rule" - | "change_history" - | "priority" - | "event_upcoming" - | "build_circle" - | "wysiwyg" - | "pan_tool" - | "api" - | "circle_notifications" - | "hotel_class" - | "manage_history" - | "web_asset" - | "accessible" - | "upgrade" - | "bookmark_add" - | "lock_reset" - | "input" - | "event_busy" - | "more_time" - | "flutter_dash" - | "model_training" - | "save_as" - | "backup" - | "accessibility" - | "dynamic_feed" - | "alarm_on" - | "pageview" - | "home_app_logo" - | "perm_contact_calendar" - | "label_important" - | "history_toggle_off" - | "approval" - | "square_foot" - | "more" - | "swipe" - | "component_exchange" - | "event_repeat" - | "bookmark_added" - | "app_shortcut" - | "unpublished" - | "open_in_browser" - | "offline_bolt" - | "notification_add" - | "no_accounts" - | "free_cancellation" - | "running_with_errors" - | "background_replace" - | "anchor" - | "webhook" - | "3d_rotation" - | "lock_person" - | "new_label" - | "lock_clock" - | "accessible_forward" - | "auto_delete" - | "add_alert" - | "domain_verification" - | "outbound" - | "hand_gesture" - | "tab" - | "settings_power" - | "chrome_reader_mode" - | "online_prediction" - | "gesture" - | "edit_notifications" - | "lightbulb_circle" - | "find_replace" - | "backup_table" - | "offline_pin" - | "wifi_protected_setup" - | "ad_units" - | "http" - | "bookmark_remove" - | "alarm_add" - | "pinch_zoom_out" - | "on_device_training" - | "snooze" - | "batch_prediction" - | "code_off" - | "pinch_zoom_in" - | "commit" - | "hourglass_disabled" - | "settings_overscan" - | "polymer" - | "target" - | "logo_dev" - | "youtube_activity" - | "time_auto" - | "voice_over_off" - | "person_add_disabled" - | "alarm_off" - | "update_disabled" - | "timer_10_alt_1" - | "rounded_corner" - | "label_off" - | "all_out" - | "timer_3_alt_1" - | "tab_unselected" - | "rsvp" - | "web_asset_off" - | "pin_invoke" - | "pin_end" - | "code_blocks" - | "approval_delegation" - | "arrow_selector_tool" - | "developer_guide" - | "feature_search" - | "reminder" - | "lists" - | "problem" - | "visibility_lock" - | "browse" - | "award_star" - | "data_loss_prevention" - | "ad_group" - | "select_window" - | "ad" - | "release_alert" - | "settings_account_box" - | "shadow" - | "pan_zoom" - | "draft_orders" - | "circles_ext" - | "ad_group_off" - | "add_ad" - | "account_circle_off" - | "gesture_select" - | "lock_open_right" - | "watch_screentime" - | "circles" - | "select_window_off" - | "shift" - | "help_clinic" - | "scrollable_header" - | "bookmark_manager" - | "ad_off" - | "alarm_smart_wake" - | "preview_off" - | "supervised_user_circle_off" - | "water_lock" - | "domain_verification_off" - | "measuring_tape" - | "warning_off" - | "info_i" - | "shift_lock" - | "mail" - | "call" - | "notifications" - | "send" - | "chat" - | "link" - | "forum" - | "inventory_2" - | "phone_in_talk" - | "contact_support" - | "chat_bubble" - | "notifications_active" - | "alternate_email" - | "sms" - | "comment" - | "power_settings_new" - | "hub" - | "person_search" - | "import_contacts" - | "contact_mail" - | "contacts" - | "live_help" - | "forward_to_inbox" - | "mark_email_unread" - | "reviews" - | "lan" - | "contact_phone" - | "mode_comment" - | "hourglass_top" - | "inbox" - | "drafts" - | "outgoing_mail" - | "hourglass_bottom" - | "mark_email_read" - | "link_off" - | "calendar_add_on" - | "add_comment" - | "phone_enabled" - | "speaker_notes" - | "perm_phone_msg" - | "g_translate" - | "co_present" - | "notifications_off" - | "topic" - | "call_end" - | "cell_tower" - | "mark_chat_unread" - | "schedule_send" - | "dialpad" - | "call_made" - | "satellite_alt" - | "mark_unread_chat_alt" - | "unarchive" - | "3p" - | "cancel_presentation" - | "mark_as_unread" - | "move_to_inbox" - | "attach_email" - | "phonelink_ring" - | "next_plan" - | "unsubscribe" - | "phone_callback" - | "call_received" - | "settings_phone" - | "call_split" - | "present_to_all" - | "add_call" - | "markunread_mailbox" - | "all_inbox" - | "phone_forwarded" - | "voice_chat" - | "mail_lock" - | "attribution" - | "voicemail" - | "duo" - | "contact_emergency" - | "mark_chat_read" - | "upcoming" - | "phone_disabled" - | "swap_calls" - | "outbox" - | "phonelink_lock" - | "spoke" - | "cancel_schedule_send" - | "ring_volume" - | "notifications_paused" - | "picture_in_picture_alt" - | "quickreply" - | "phone_missed" - | "comment_bank" - | "send_and_archive" - | "chat_add_on" - | "settings_bluetooth" - | "phonelink_erase" - | "picture_in_picture" - | "comments_disabled" - | "video_chat" - | "score" - | "pause_presentation" - | "cell_wifi" - | "speaker_phone" - | "speaker_notes_off" - | "auto_read_play" - | "mms" - | "call_merge" - | "play_for_work" - | "call_missed_outgoing" - | "call_missed" - | "wifi_channel" - | "calendar_apps_script" - | "phone_paused" - | "rtt" - | "auto_read_pause" - | "phone_locked" - | "chat_apps_script" - | "wifi_calling" - | "dialer_sip" - | "nat" - | "sip" - | "phone_bluetooth_speaker" - | "e911_avatar" - | "inbox_customize" - | "stacked_email" - | "tooltip" - | "business_messages" - | "notification_multiple" - | "chat_error" - | "ods" - | "chat_paste_go" - | "signal_cellular_add" - | "call_log" - | "outbox_alt" - | "call_quality" - | "odt" - | "stacked_inbox" - | "phonelink_ring_off" - | "network_manage" - | "wifi_proxy" - | "network_intelligence_history" - | "wifi_add" - | "network_intelligence_update" - | "edit" - | "photo_camera" - | "filter_alt" - | "image" - | "navigate_next" - | "tune" - | "timer" - | "picture_as_pdf" - | "circle" - | "palette" - | "add_a_photo" - | "photo_library" - | "navigate_before" - | "auto_stories" - | "add_photo_alternate" - | "brush" - | "imagesmode" - | "nature" - | "flash_on" - | "wb_sunny" - | "camera" - | "straighten" - | "looks_one" - | "landscape" - | "timelapse" - | "slideshow" - | "grid_on" - | "rotate_right" - | "crop_square" - | "adjust" - | "style" - | "crop_free" - | "aspect_ratio" - | "brightness_6" - | "photo" - | "nature_people" - | "filter_vintage" - | "image_search" - | "crop" - | "blur_on" - | "center_focus_strong" - | "contrast" - | "compare" - | "looks_two" - | "rotate_left" - | "colorize" - | "flare" - | "filter_none" - | "wb_incandescent" - | "filter_drama" - | "healing" - | "looks_3" - | "wb_twilight" - | "brightness_5" - | "invert_colors" - | "lens" - | "animation" - | "opacity" - | "incomplete_circle" - | "broken_image" - | "filter_center_focus" - | "add_to_photos" - | "brightness_4" - | "flip" - | "flash_off" - | "center_focus_weak" - | "auto_awesome_motion" - | "mic_external_on" - | "flip_camera_android" - | "lens_blur" - | "no_photography" - | "details" - | "grain" - | "image_not_supported" - | "web_stories" - | "panorama" - | "dehaze" - | "gif_box" - | "flaky" - | "loupe" - | "exposure_plus_1" - | "settings_brightness" - | "texture" - | "auto_awesome_mosaic" - | "looks_4" - | "filter_1" - | "timer_off" - | "flip_camera_ios" - | "panorama_fish_eye" - | "view_compact" - | "brightness_1" - | "filter" - | "control_point_duplicate" - | "photo_camera_front" - | "brightness_7" - | "photo_album" - | "transform" - | "linked_camera" - | "view_comfy" - | "crop_16_9" - | "looks" - | "hide_image" - | "looks_5" - | "exposure" - | "rotate_90_degrees_ccw" - | "filter_hdr" - | "gif" - | "brightness_3" - | "hdr_strong" - | "leak_add" - | "crop_7_5" - | "gradient" - | "hdr_auto" - | "crop_portrait" - | "vrpano" - | "camera_roll" - | "blur_circular" - | "motion_photos_auto" - | "rotate_90_degrees_cw" - | "photo_size_select_small" - | "brightness_2" - | "shutter_speed" - | "looks_6" - | "flash_auto" - | "camera_front" - | "crop_landscape" - | "filter_2" - | "filter_tilt_shift" - | "monochrome_photos" - | "deblur" - | "night_sight_auto" - | "crop_5_4" - | "hdr_weak" - | "filter_4" - | "motion_photos_paused" - | "filter_3" - | "crop_rotate" - | "crop_3_2" - | "tonality" - | "switch_camera" - | "photo_frame" - | "exposure_zero" - | "ev_shadow" - | "fluorescent" - | "macro_off" - | "photo_size_select_large" - | "filter_frames" - | "party_mode" - | "raw_on" - | "motion_blur" - | "exposure_plus_2" - | "photo_camera_back" - | "blur_linear" - | "exposure_neg_1" - | "wb_iridescent" - | "filter_b_and_w" - | "switch_video" - | "motion_photos_off" - | "panorama_horizontal" - | "filter_5" - | "blur_medium" - | "invert_colors_off" - | "face_retouching_off" - | "filter_7" - | "burst_mode" - | "panorama_photosphere" - | "hdr_on" - | "grid_off" - | "filter_9_plus" - | "filter_8" - | "blur_short" - | "timer_10" - | "filter_9" - | "dirty_lens" - | "wb_shade" - | "no_flash" - | "filter_6" - | "image_aspect_ratio" - | "trail_length" - | "exposure_neg_2" - | "vignette" - | "timer_3" - | "leak_remove" - | "60fps_select" - | "blur_off" - | "30fps_select" - | "perm_camera_mic" - | "mic_external_off" - | "trail_length_medium" - | "camera_rear" - | "panorama_vertical" - | "trail_length_short" - | "night_sight_auto_off" - | "autofps_select" - | "panorama_wide_angle" - | "mp" - | "hdr_off" - | "24mp" - | "hdr_on_select" - | "hdr_enhanced_select" - | "22mp" - | "10mp" - | "12mp" - | "18mp" - | "hdr_auto_select" - | "hdr_plus" - | "raw_off" - | "wb_auto" - | "9mp" - | "13mp" - | "20mp" - | "5mp" - | "7mp" - | "15mp" - | "hdr_off_select" - | "16mp" - | "hevc" - | "19mp" - | "14mp" - | "23mp" - | "2mp" - | "8mp" - | "3mp" - | "6mp" - | "11mp" - | "21mp" - | "17mp" - | "4mp" - | "gallery_thumbnail" - | "motion_photos_on" - | "masked_transitions" - | "photo_prints" - | "settings_photo_camera" - | "planner_banner_ad_pt" - | "settings_panorama" - | "unknown_2" - | "vr180_create2d" - | "settings_video_camera" - | "motion_mode" - | "settings_motion_mode" - | "settings_night_sight" - | "50mp" - | "background_dot_large" - | "background_grid_small" - | "settings_cinematic_blur" - | "settings_timelapse" - | "macro_auto" - | "settings_b_roll" - | "high_density" - | "contrast_rtl_off" - | "low_density" - | "settings_slow_motion" - | "shopping_cart" - | "payments" - | "shopping_bag" - | "monitoring" - | "credit_card" - | "receipt_long" - | "attach_money" - | "storefront" - | "sell" - | "trending_up" - | "database" - | "account_balance" - | "work" - | "paid" - | "account_balance_wallet" - | "analytics" - | "query_stats" - | "store" - | "savings" - | "monetization_on" - | "calculate" - | "qr_code_scanner" - | "bar_chart" - | "add_shopping_cart" - | "account_tree" - | "receipt" - | "redeem" - | "currency_exchange" - | "trending_flat" - | "shopping_basket" - | "qr_code_2" - | "domain" - | "precision_manufacturing" - | "qr_code" - | "leaderboard" - | "corporate_fare" - | "timeline" - | "insert_chart" - | "currency_rupee" - | "show_chart" - | "wallet" - | "euro" - | "work_history" - | "meeting_room" - | "credit_score" - | "barcode_scanner" - | "pie_chart" - | "loyalty" - | "copyright" - | "barcode" - | "conversion_path" - | "track_changes" - | "trending_down" - | "price_check" - | "euro_symbol" - | "schema" - | "add_business" - | "add_card" - | "card_membership" - | "currency_bitcoin" - | "price_change" - | "production_quantity_limits" - | "donut_large" - | "tenancy" - | "data_exploration" - | "bubble_chart" - | "donut_small" - | "contactless" - | "money" - | "stacked_line_chart" - | "stacked_bar_chart" - | "toll" - | "money_off" - | "cases" - | "currency_yen" - | "currency_pound" - | "area_chart" - | "atr" - | "remove_shopping_cart" - | "room_preferences" - | "add_chart" - | "shop" - | "domain_add" - | "card_travel" - | "grouped_bar_chart" - | "legend_toggle" - | "scatter_plot" - | "credit_card_off" - | "mediation" - | "ssid_chart" - | "candlestick_chart" - | "waterfall_chart" - | "currency_ruble" - | "full_stacked_bar_chart" - | "domain_disabled" - | "strikethrough_s" - | "shop_two" - | "next_week" - | "atm" - | "multiline_chart" - | "currency_lira" - | "currency_yuan" - | "no_meeting_room" - | "currency_franc" - | "troubleshoot" - | "finance" - | "data_table" - | "bid_landscape" - | "contactless_off" - | "bar_chart_4_bars" - | "universal_currency_alt" - | "chart_data" - | "podium" - | "order_approve" - | "family_history" - | "conveyor_belt" - | "send_money" - | "flowsheet" - | "forklift" - | "qr_code_2_add" - | "front_loader" - | "inactive_order" - | "pallet" - | "bid_landscape_disabled" - | "barcode_reader" - | "box" - | "trolley" - | "box_add" - | "conversion_path_off" - | "order_play" - | "work_alert" - | "box_edit" - | "work_update" - | "pin_drop" - | "location_on" - | "map" - | "home_pin" - | "explore" - | "restaurant" - | "flag" - | "my_location" - | "local_fire_department" - | "person_pin_circle" - | "local_mall" - | "near_me" - | "where_to_vote" - | "business_center" - | "east" - | "restaurant_menu" - | "handyman" - | "factory" - | "local_library" - | "medical_services" - | "home_work" - | "layers" - | "local_activity" - | "share_location" - | "emergency" - | "north_east" - | "add_location" - | "fastfood" - | "navigation" - | "warehouse" - | "person_pin" - | "local_parking" - | "home_repair_service" - | "local_hospital" - | "south" - | "local_police" - | "zoom_out_map" - | "location_searching" - | "local_florist" - | "location_away" - | "crisis_alert" - | "west" - | "local_gas_station" - | "park" - | "maps_ugc" - | "cleaning_services" - | "local_atm" - | "package" - | "360" - | "electrical_services" - | "north" - | "flag_circle" - | "add_location_alt" - | "directions" - | "fmd_bad" - | "theater_comedy" - | "local_drink" - | "location_home" - | "local_pizza" - | "local_post_office" - | "not_listed_location" - | "wine_bar" - | "beenhere" - | "local_convenience_store" - | "signpost" - | "alt_route" - | "tour" - | "trip_origin" - | "church" - | "traffic" - | "local_laundry_service" - | "safety_check" - | "ev_station" - | "takeout_dining" - | "moving" - | "zoom_in_map" - | "soup_kitchen" - | "pest_control" - | "stadium" - | "transfer_within_a_station" - | "location_off" - | "connecting_airports" - | "wrong_location" - | "multiple_stop" - | "edit_location" - | "plumbing" - | "mode_of_travel" - | "minor_crash" - | "south_east" - | "add_road" - | "local_pharmacy" - | "fire_truck" - | "castle" - | "dry_cleaning" - | "set_meal" - | "baby_changing_station" - | "edit_location_alt" - | "layers_clear" - | "mosque" - | "north_west" - | "local_car_wash" - | "edit_attributes" - | "run_circle" - | "transit_enterexit" - | "sos" - | "satellite" - | "edit_road" - | "south_west" - | "streetview" - | "add_home" - | "kebab_dining" - | "airline_stops" - | "local_see" - | "fire_hydrant" - | "assist_walker" - | "add_home_work" - | "flight_class" - | "remove_road" - | "no_meals" - | "synagogue" - | "fort" - | "temple_buddhist" - | "location_disabled" - | "compass_calibration" - | "temple_hindu" - | "explore_off" - | "pest_control_rodent" - | "near_me_disabled" - | "directions_alt" - | "pergola" - | "directions_off" - | "directions_alt_off" - | "pet_supplies" - | "moved_location" - | "move_location" - | "moving_ministry" - | "move" - | "safety_check_off" - | "description" - | "content_copy" - | "dashboard" - | "edit_note" - | "menu_book" - | "grid_view" - | "list" - | "folder" - | "list_alt" - | "inventory" - | "folder_open" - | "article" - | "fact_check" - | "attach_file" - | "format_list_bulleted" - | "assignment" - | "task" - | "checklist" - | "cloud_upload" - | "draft" - | "summarize" - | "feed" - | "draw" - | "cloud" - | "newspaper" - | "view_list" - | "file_copy" - | "note_add" - | "border_color" - | "book" - | "history_edu" - | "design_services" - | "pending_actions" - | "format_quote" - | "post_add" - | "request_quote" - | "cloud_download" - | "drag_handle" - | "contact_page" - | "table" - | "space_dashboard" - | "archive" - | "content_paste" - | "percent" - | "attachment" - | "assignment_ind" - | "format_list_numbered" - | "assignment_turned_in" - | "tag" - | "table_chart" - | "sticky_note_2" - | "text_fields" - | "dashboard_customize" - | "reorder" - | "integration_instructions" - | "format_bold" - | "find_in_page" - | "text_snippet" - | "note" - | "document_scanner" - | "checklist_rtl" - | "note_alt" - | "edit_document" - | "cloud_sync" - | "table_rows" - | "perm_media" - | "cloud_done" - | "title" - | "table_view" - | "content_cut" - | "notes" - | "cut" - | "data_object" - | "subject" - | "functions" - | "format_italic" - | "content_paste_search" - | "format_color_fill" - | "folder_shared" - | "plagiarism" - | "horizontal_rule" - | "file_present" - | "folder_copy" - | "format_align_left" - | "ballot" - | "team_dashboard" - | "format_paint" - | "cloud_off" - | "add_link" - | "view_column" - | "read_more" - | "difference" - | "view_agenda" - | "format_size" - | "format_underlined" - | "vertical_align_top" - | "toc" - | "height" - | "vertical_align_bottom" - | "copy_all" - | "view_week" - | "drive_folder_upload" - | "assignment_late" - | "format_color_text" - | "view_module" - | "drive_file_move" - | "low_priority" - | "assignment_return" - | "format_align_center" - | "segment" - | "folder_special" - | "calendar_view_month" - | "folder_zip" - | "polyline" - | "square" - | "breaking_news_alt_1" - | "format_align_right" - | "grading" - | "view_headline" - | "linear_scale" - | "edit_off" - | "view_quilt" - | "view_carousel" - | "text_increase" - | "request_page" - | "view_sidebar" - | "pages" - | "text_format" - | "format_align_justify" - | "calendar_view_week" - | "hexagon" - | "numbers" - | "docs_add_on" - | "folder_delete" - | "format_shapes" - | "forms_add_on" - | "imagesearch_roller" - | "calendar_view_day" - | "video_file" - | "font_download" - | "format_list_numbered_rtl" - | "join_inner" - | "add_to_drive" - | "content_paste_go" - | "restore_page" - | "rectangle" - | "vertical_split" - | "rule_folder" - | "format_color_reset" - | "cloud_circle" - | "view_stream" - | "format_indent_increase" - | "spellcheck" - | "assignment_returned" - | "data_array" - | "align_horizontal_left" - | "text_decrease" - | "pivot_table_chart" - | "deselect" - | "vertical_align_center" - | "pentagon" - | "merge_type" - | "space_bar" - | "format_strikethrough" - | "view_day" - | "flip_to_front" - | "join_left" - | "short_text" - | "border_all" - | "shape_line" - | "format_line_spacing" - | "line_weight" - | "horizontal_split" - | "format_indent_decrease" - | "align_horizontal_center" - | "join_right" - | "snippet_folder" - | "subtitles_off" - | "align_vertical_bottom" - | "folder_off" - | "align_horizontal_right" - | "glyphs" - | "format_clear" - | "function" - | "insert_page_break" - | "vertical_distribute" - | "content_paste_off" - | "superscript" - | "horizontal_distribute" - | "line_axis" - | "line_style" - | "flip_to_back" - | "align_vertical_center" - | "align_vertical_top" - | "margin" - | "clarify" - | "wrap_text" - | "view_array" - | "subscript" - | "border_clear" - | "border_style" - | "amp_stories" - | "border_outer" - | "type_specimen" - | "text_rotate_vertical" - | "padding" - | "forms_apps_script" - | "border_vertical" - | "ink_pen" - | "text_rotation_none" - | "counter_1" - | "format_textdirection_l_to_r" - | "ink_eraser" - | "format_overline" - | "docs_apps_script" - | "border_horizontal" - | "font_download_off" - | "format_textdirection_r_to_l" - | "newsmode" - | "text_rotation_angleup" - | "border_bottom" - | "text_rotation_down" - | "border_inner" - | "border_top" - | "border_left" - | "text_rotation_angledown" - | "assignment_add" - | "finance_chip" - | "text_rotate_up" - | "news" - | "border_right" - | "format_h1" - | "ink_highlighter" - | "view_column_2" - | "join" - | "full_coverage" - | "overview" - | "format_underlined_squiggle" - | "colors" - | "slide_library" - | "format_h2" - | "format_paragraph" - | "format_image_left" - | "format_list_bulleted_add" - | "breaking_news" - | "counter_2" - | "lab_profile" - | "frame_inspect" - | "equal" - | "variables" - | "counter_3" - | "format_image_right" - | "format_h3" - | "ink_marker" - | "format_h5" - | "format_h6" - | "csv" - | "voting_chip" - | "process_chart" - | "remove_selection" - | "format_h4" - | "signature" - | "format_ink_highlighter" - | "location_chip" - | "export_notes" - | "stylus_laser_pointer" - | "sticky_note" - | "shapes" - | "unknown_document" - | "frame_source" - | "add_notes" - | "counter_4" - | "format_text_overflow" - | "cell_merge" - | "format_letter_spacing_standard" - | "grid_guides" - | "select" - | "table_rows_narrow" - | "diagnosis" - | "regular_expression" - | "reset_image" - | "table_chart_view" - | "text_select_move_forward_character" - | "business_chip" - | "custom_typography" - | "draw_abstract" - | "source_notes" - | "decimal_increase" - | "folder_managed" - | "list_alt_add" - | "text_ad" - | "width" - | "insert_text" - | "lasso_select" - | "scan_delete" - | "smb_share" - | "ungroup" - | "counter_5" - | "format_letter_spacing_2" - | "line_end_arrow_notch" - | "line_start" - | "tab_close" - | "thumbnail_bar" - | "contract" - | "counter_6" - | "format_letter_spacing" - | "line_end" - | "match_case" - | "stroke_full" - | "draw_collage" - | "flex_wrap" - | "format_letter_spacing_wider" - | "language_chinese_quick" - | "line_end_square" - | "other_admission" - | "post" - | "scan" - | "text_select_start" - | "folder_supervised" - | "format_letter_spacing_wide" - | "language_spanish" - | "line_end_arrow" - | "line_start_diamond" - | "match_word" - | "align_justify_space_around" - | "align_justify_space_between" - | "align_space_between" - | "contract_edit" - | "format_text_clip" - | "line_end_circle" - | "line_start_circle" - | "special_character" - | "tab_duplicate" - | "tab_move" - | "tab_new_right" - | "text_select_jump_to_beginning" - | "top_panel_close" - | "top_panel_open" - | "counter_0" - | "counter_7" - | "counter_8" - | "flex_direction" - | "frame_reload" - | "heap_snapshot_multiple" - | "heap_snapshot_thumbnail" - | "ink_eraser_off" - | "language_french" - | "language_gb_english" - | "language_international" - | "language_korean_latin" - | "line_end_diamond" - | "sheets_rtl" - | "text_select_move_forward_word" - | "text_select_move_up" - | "tsv" - | "align_justify_space_even" - | "attach_file_add" - | "counter_9" - | "fit_width" - | "heap_snapshot_large" - | "language_chinese_dayi" - | "line_curve" - | "line_start_square" - | "person_book" - | "stroke_partial" - | "tab_group" - | "text_select_move_down" - | "align_justify_center" - | "align_justify_flex_end" - | "align_justify_flex_start" - | "align_space_around" - | "align_space_even" - | "contract_delete" - | "fit_page" - | "format_text_wrap" - | "highlighter_size_4" - | "language_chinese_array" - | "language_chinese_cangjie" - | "language_chinese_pinyin" - | "language_chinese_wubi" - | "language_pinyin" - | "language_us" - | "language_us_colemak" - | "language_us_dvorak" - | "letter_switch" - | "line_start_arrow" - | "line_start_arrow_notch" - | "pen_size_2" - | "pen_size_3" - | "pen_size_4" - | "pen_size_5" - | "tab_close_right" - | "tab_recent" - | "text_select_end" - | "text_select_jump_to_end" - | "align_center" - | "align_end" - | "align_flex_center" - | "align_flex_end" - | "align_flex_start" - | "align_items_stretch" - | "align_justify_stretch" - | "align_self_stretch" - | "align_start" - | "align_stretch" - | "decimal_decrease" - | "flex_no_wrap" - | "highlighter_size_1" - | "highlighter_size_2" - | "highlighter_size_3" - | "highlighter_size_5" - | "pen_size_1" - | "text_select_move_back_character" - | "text_select_move_back_word" - | "play_arrow" - | "play_circle" - | "mic" - | "videocam" - | "volume_up" - | "pause" - | "music_note" - | "library_books" - | "movie" - | "skip_next" - | "speed" - | "replay" - | "volume_off" - | "pause_circle" - | "view_in_ar" - | "fiber_manual_record" - | "skip_previous" - | "stop_circle" - | "stop" - | "equalizer" - | "subscriptions" - | "video_library" - | "fast_forward" - | "playlist_add" - | "video_call" - | "repeat" - | "volume_mute" - | "shuffle" - | "mic_off" - | "hearing" - | "library_music" - | "playlist_add_check" - | "podcasts" - | "fast_rewind" - | "sound_detection_dog_barking" - | "queue_music" - | "video_camera_front" - | "subtitles" - | "volume_down" - | "play_pause" - | "album" - | "discover_tune" - | "radio" - | "av_timer" - | "library_add_check" - | "videocam_off" - | "closed_caption" - | "stream" - | "forward_10" - | "not_started" - | "playlist_play" - | "replay_10" - | "fiber_new" - | "branding_watermark" - | "text_to_speech" - | "recent_actors" - | "playlist_remove" - | "interpreter_mode" - | "slow_motion_video" - | "frame_person" - | "playlist_add_check_circle" - | "settings_voice" - | "video_settings" - | "featured_play_list" - | "sound_detection_loud_sound" - | "audio_file" - | "lyrics" - | "play_lesson" - | "hd" - | "repeat_one" - | "call_to_action" - | "high_quality" - | "add_to_queue" - | "music_off" - | "video_camera_back" - | "spatial_audio_off" - | "shuffle_on" - | "playlist_add_circle" - | "volume_down_alt" - | "hearing_disabled" - | "featured_video" - | "replay_5" - | "repeat_on" - | "queue_play_next" - | "speech_to_text" - | "spatial_audio" - | "art_track" - | "explicit" - | "airplay" - | "forward_5" - | "forward_30" - | "4k" - | "music_video" - | "replay_30" - | "control_camera" - | "spatial_tracking" - | "closed_caption_disabled" - | "digital_out_of_home" - | "video_label" - | "fiber_smart_record" - | "play_disabled" - | "repeat_one_on" - | "broadcast_on_personal" - | "sd" - | "missed_video_call" - | "surround_sound" - | "fiber_pin" - | "10k" - | "sound_detection_glass_break" - | "60fps" - | "remove_from_queue" - | "brand_awareness" - | "broadcast_on_home" - | "fiber_dvr" - | "30fps" - | "4k_plus" - | "video_stable" - | "8k" - | "1k" - | "privacy" - | "8k_plus" - | "2k" - | "instant_mix" - | "7k" - | "1k_plus" - | "9k" - | "9k_plus" - | "5k" - | "6k" - | "2k_plus" - | "5k_plus" - | "6k_plus" - | "3k" - | "7k_plus" - | "3k_plus" - | "ar_on_you" - | "no_sound" - | "cinematic_blur" - | "video_search" - | "hangout_video" - | "genres" - | "media_link" - | "mic_double" - | "autoplay" - | "video_camera_front_off" - | "movie_edit" - | "autopause" - | "forward_media" - | "movie_info" - | "resume" - | "hangout_video_off" - | "select_to_speak" - | "autostop" - | "2d" - | "forward_circle" - | "view_in_ar_off" - | "frame_person_off" - | "sound_sampler" - | "local_shipping" - | "directions_car" - | "flight" - | "directions_run" - | "directions_walk" - | "flight_takeoff" - | "directions_bus" - | "directions_bike" - | "train" - | "airport_shuttle" - | "pedal_bike" - | "directions_boat" - | "two_wheeler" - | "agriculture" - | "local_taxi" - | "sailing" - | "electric_car" - | "flight_land" - | "hail" - | "no_crash" - | "commute" - | "motorcycle" - | "car_crash" - | "tram" - | "departure_board" - | "subway" - | "electric_moped" - | "turn_right" - | "electric_scooter" - | "fork_right" - | "directions_subway" - | "tire_repair" - | "electric_bike" - | "rv_hookup" - | "bus_alert" - | "turn_left" - | "transportation" - | "airlines" - | "taxi_alert" - | "u_turn_left" - | "directions_railway" - | "electric_rickshaw" - | "turn_slight_right" - | "u_turn_right" - | "fork_left" - | "railway_alert" - | "bike_scooter" - | "turn_sharp_right" - | "turn_slight_left" - | "no_transfer" - | "snowmobile" - | "turn_sharp_left" - | "flightsmode" - | "swap_driving_apps_wheel" - | "ambulance" - | "school" - | "campaign" - | "construction" - | "engineering" - | "volunteer_activism" - | "science" - | "sports_esports" - | "confirmation_number" - | "real_estate_agent" - | "cake" - | "self_improvement" - | "sports_soccer" - | "air" - | "biotech" - | "water" - | "hiking" - | "architecture" - | "sports_score" - | "personal_injury" - | "sports_basketball" - | "waves" - | "theaters" - | "sports_tennis" - | "switch_account" - | "nights_stay" - | "sports_gymnastics" - | "how_to_vote" - | "backpack" - | "sports_motorsports" - | "sports_kabaddi" - | "surfing" - | "piano" - | "sports" - | "toys" - | "sports_volleyball" - | "sports_baseball" - | "sports_martial_arts" - | "camping" - | "downhill_skiing" - | "swords" - | "kayaking" - | "scoreboard" - | "phishing" - | "sports_handball" - | "sports_football" - | "skateboarding" - | "sports_golf" - | "sports_cricket" - | "toys_fan" - | "nordic_walking" - | "kitesurfing" - | "roller_skating" - | "rowing" - | "scuba_diving" - | "trophy" - | "storm" - | "sports_mma" - | "paragliding" - | "snowboarding" - | "sports_hockey" - | "ice_skating" - | "snowshoeing" - | "sports_rugby" - | "sledding" - | "piano_off" - | "no_backpack" - | "family_link" - | "rewarded_ads" - | "ifl" - | "cake_add" - | "mindfulness" - | "health_metrics" - | "steps" - | "sprint" - | "exercise" - | "stress_management" - | "menstrual_health" - | "readiness_score" - | "relax" - | "ecg_heart" - | "laps" - | "azm" - | "pace" - | "distance" - | "person_play" - | "floor" - | "avg_time" - | "person_celebrate" - | "avg_pace" - | "fertile" - | "onsen" - | "podiatry" - | "spo2" - | "water_full" - | "bath_outdoor" - | "play_shapes" - | "eda" - | "bia" - | "water_medium" - | "interactive_space" - | "elevation" - | "hr_resting" - | "glass_cup" - | "water_loss" - | "monitor_weight_loss" - | "sauna" - | "sleep_score" - | "bath_private" - | "monitor_weight_gain" - | "bath_public_large" - | "check_in_out" - | "physical_therapy" - | "phone_iphone" - | "save" - | "smartphone" - | "print" - | "keyboard_arrow_down" - | "computer" - | "devices" - | "desktop_windows" - | "smart_display" - | "dns" - | "keyboard_backspace" - | "headphones" - | "smart_toy" - | "phone_android" - | "keyboard_arrow_right" - | "memory" - | "keyboard" - | "live_tv" - | "laptop_mac" - | "headset_mic" - | "keyboard_arrow_up" - | "tv" - | "device_thermostat" - | "mouse" - | "balance" - | "route" - | "point_of_sale" - | "keyboard_arrow_left" - | "laptop_chromebook" - | "keyboard_return" - | "watch" - | "power" - | "laptop_windows" - | "router" - | "developer_board" - | "display_settings" - | "scale" - | "book_online" - | "developer_mode" - | "fax" - | "cast" - | "cast_for_education" - | "videogame_asset" - | "device_hub" - | "straight" - | "screen_search_desktop" - | "desktop_mac" - | "mobile_friendly" - | "settings_ethernet" - | "settings_input_antenna" - | "monitor" - | "important_devices" - | "tablet_mac" - | "devices_other" - | "send_to_mobile" - | "system_update" - | "settings_remote" - | "monitor_weight" - | "screen_rotation" - | "screen_share" - | "keyboard_alt" - | "settings_input_component" - | "speaker" - | "merge" - | "sim_card" - | "keyboard_tab" - | "vibration" - | "power_off" - | "tablet" - | "connected_tv" - | "screenshot_monitor" - | "remember_me" - | "browser_updated" - | "security_update_good" - | "sd_card" - | "cast_connected" - | "device_unknown" - | "tablet_android" - | "charging_station" - | "phonelink_setup" - | "punch_clock" - | "scanner" - | "screenshot" - | "settings_input_hdmi" - | "stay_current_portrait" - | "tap_and_play" - | "keyboard_hide" - | "print_disabled" - | "security_update_warning" - | "disc_full" - | "app_blocking" - | "keyboard_capslock" - | "speaker_group" - | "mobile_screen_share" - | "aod" - | "sd_card_alert" - | "tty" - | "add_to_home_screen" - | "lift_to_talk" - | "earbuds" - | "perm_device_information" - | "stop_screen_share" - | "mobile_off" - | "headset_off" - | "desktop_access_disabled" - | "reset_tv" - | "offline_share" - | "adf_scanner" - | "headphones_battery" - | "screen_lock_portrait" - | "roundabout_right" - | "dock" - | "settop_component" - | "settings_input_svideo" - | "watch_off" - | "smart_screen" - | "stay_current_landscape" - | "chromecast_device" - | "settings_cell" - | "earbuds_battery" - | "home_max" - | "power_input" - | "no_sim" - | "screen_lock_landscape" - | "ramp_right" - | "roundabout_left" - | "stay_primary_landscape" - | "stay_primary_portrait" - | "developer_board_off" - | "tv_off" - | "home_mini" - | "phonelink_off" - | "ramp_left" - | "screen_lock_rotation" - | "videogame_asset_off" - | "aod_tablet" - | "open_in_phone" - | "gamepad" - | "robot" - | "rear_camera" - | "jamboard_kiosk" - | "mimo" - | "app_promo" - | "devices_wearables" - | "tv_options_edit_channels" - | "developer_mode_tv" - | "mimo_disconnect" - | "ambient_screen" - | "touchpad_mouse" - | "camera_video" - | "tv_signin" - | "aod_watch" - | "joystick" - | "ecg" - | "memory_alt" - | "robot_2" - | "tv_guide" - | "devices_off" - | "night_sight_max" - | "hard_drive" - | "open_jam" - | "screenshot_tablet" - | "stream_apps" - | "cast_pause" - | "cast_warning" - | "keyboard_tab_rtl" - | "pacemaker" - | "deskphone" - | "watch_wake" - | "hard_drive_2" - | "lda" - | "print_lock" - | "tv_remote" - | "watch_button_press" - | "audio_video_receiver" - | "print_add" - | "print_connect" - | "print_error" - | "ventilator" - | "dark_mode" - | "light_mode" - | "wifi" - | "signal_cellular_alt" - | "password" - | "widgets" - | "pin" - | "storage" - | "rss_feed" - | "android" - | "wifi_off" - | "bluetooth" - | "battery_charging_full" - | "dvr" - | "thermostat" - | "graphic_eq" - | "nightlight" - | "battery_5_bar" - | "signal_wifi_4_bar" - | "gpp_maybe" - | "cable" - | "gpp_bad" - | "data_usage" - | "battery_4_bar" - | "signal_cellular_4_bar" - | "radar" - | "airplanemode_active" - | "battery_0_bar" - | "cameraswitch" - | "wallpaper" - | "signal_disconnected" - | "flashlight_on" - | "network_check" - | "battery_6_bar" - | "charger" - | "wifi_tethering" - | "sim_card_download" - | "usb" - | "quick_phrases" - | "splitscreen" - | "battery_3_bar" - | "battery_1_bar" - | "adb" - | "network_wifi_3_bar" - | "battery_low" - | "battery_alert" - | "bluetooth_searching" - | "network_wifi" - | "bluetooth_connected" - | "5g" - | "wifi_find" - | "battery_2_bar" - | "brightness_high" - | "network_cell" - | "pattern" - | "nfc" - | "data_saver_on" - | "bluetooth_disabled" - | "signal_wifi_statusbar_not_connected" - | "signal_wifi_bad" - | "signal_cellular_3_bar" - | "network_wifi_2_bar" - | "noise_control_off" - | "network_wifi_1_bar" - | "brightness_medium" - | "signal_wifi_off" - | "battery_very_low" - | "mode_standby" - | "brightness_low" - | "mobiledata_off" - | "signal_wifi_0_bar" - | "battery_charging_20" - | "battery_charging_80" - | "grid_4x4" - | "battery_saver" - | "battery_charging_90" - | "flashlight_off" - | "signal_wifi_statusbar_null" - | "battery_charging_50" - | "settings_system_daydream" - | "battery_unknown" - | "signal_cellular_2_bar" - | "screen_rotation_alt" - | "wifi_calling_3" - | "signal_cellular_1_bar" - | "badge_critical_battery" - | "4g_mobiledata" - | "wifi_lock" - | "noise_aware" - | "do_not_disturb_on_total_silence" - | "battery_charging_60" - | "signal_cellular_connected_no_internet_0_bar" - | "nearby_error" - | "signal_cellular_0_bar" - | "battery_charging_30" - | "network_ping" - | "signal_cellular_connected_no_internet_4_bar" - | "wifi_tethering_error" - | "brightness_auto" - | "wifi_calling_1" - | "edgesensor_high" - | "wifi_2_bar" - | "airplanemode_inactive" - | "signal_cellular_nodata" - | "1x_mobiledata" - | "grid_3x3" - | "lte_mobiledata" - | "perm_data_setting" - | "signal_cellular_alt_2_bar" - | "bluetooth_drive" - | "devices_fold" - | "perm_scan_wifi" - | "network_locked" - | "media_bluetooth_on" - | "wifi_calling_2" - | "4g_plus_mobiledata" - | "signal_cellular_off" - | "timer_10_select" - | "wifi_tethering_off" - | "signal_cellular_alt_1_bar" - | "edgesensor_low" - | "3g_mobiledata" - | "usb_off" - | "wifi_1_bar" - | "apk_install" - | "signal_cellular_null" - | "lte_plus_mobiledata" - | "grid_goldenratio" - | "g_mobiledata" - | "portable_wifi_off" - | "noise_control_on" - | "media_bluetooth_off" - | "timer_3_select" - | "e_mobiledata" - | "apk_document" - | "nearby_off" - | "h_mobiledata" - | "r_mobiledata" - | "h_plus_mobiledata" - | "dual_screen" - | "nearby" - | "dock_to_left" - | "stylus" - | "stylus_note" - | "screenshot_region" - | "dock_to_right" - | "overview_key" - | "keyboard_keys" - | "battery_status_good" - | "brightness_alert" - | "brightness_empty" - | "splitscreen_left" - | "splitscreen_right" - | "keyboard_off" - | "screen_record" - | "screenshot_keyboard" - | "dock_to_bottom" - | "keyboard_external_input" - | "magnify_fullscreen" - | "wallpaper_slideshow" - | "1x_mobiledata_badge" - | "battery_change" - | "display_external_input" - | "magnify_docked" - | "screenshot_frame" - | "backlight_low" - | "battery_plus" - | "keyboard_full" - | "keyboard_onscreen" - | "wifi_notification" - | "4g_mobiledata_badge" - | "5g_mobiledata_badge" - | "keyboard_capslock_badge" - | "keyboard_previous_language" - | "lte_mobiledata_badge" - | "lte_plus_mobiledata_badge" - | "screen_rotation_up" - | "wifi_home" - | "3g_mobiledata_badge" - | "backlight_high" - | "battery_error" - | "battery_share" - | "e_mobiledata_badge" - | "ev_mobiledata_badge" - | "g_mobiledata_badge" - | "grid_3x3_off" - | "h_mobiledata_badge" - | "h_plus_mobiledata_badge" - | "signal_cellular_pause" - | "splitscreen_bottom" - | "splitscreen_top" - | "badge" - | "verified_user" - | "admin_panel_settings" - | "report" - | "security" - | "vpn_key" - | "shield" - | "policy" - | "exclamation" - | "privacy_tip" - | "assured_workload" - | "vpn_lock" - | "disabled_visible" - | "e911_emergency" - | "enhanced_encryption" - | "private_connectivity" - | "vpn_key_off" - | "add_moderator" - | "no_encryption" - | "sync_lock" - | "wifi_password" - | "key_visualizer" - | "remove_moderator" - | "encrypted" - | "report_off" - | "shield_lock" - | "shield_person" - | "vpn_key_alert" - | "shield_locked" - | "apartment" - | "location_city" - | "fitness_center" - | "lunch_dining" - | "spa" - | "cottage" - | "local_cafe" - | "hotel" - | "family_restroom" - | "beach_access" - | "local_bar" - | "pool" - | "other_houses" - | "luggage" - | "liquor" - | "airplane_ticket" - | "casino" - | "sports_bar" - | "bakery_dining" - | "ramen_dining" - | "nightlife" - | "local_dining" - | "holiday_village" - | "icecream" - | "escalator_warning" - | "dinner_dining" - | "museum" - | "food_bank" - | "night_shelter" - | "festival" - | "attractions" - | "golf_course" - | "stairs" - | "villa" - | "smoke_free" - | "smoking_rooms" - | "car_rental" - | "airline_seat_recline_normal" - | "elevator" - | "gite" - | "child_friendly" - | "airline_seat_recline_extra" - | "breakfast_dining" - | "carpenter" - | "car_repair" - | "cabin" - | "brunch_dining" - | "no_food" - | "do_not_touch" - | "houseboat" - | "rice_bowl" - | "tapas" - | "wheelchair_pickup" - | "bento" - | "no_drinks" - | "do_not_step" - | "bungalow" - | "airline_seat_flat" - | "airline_seat_individual_suite" - | "escalator" - | "chalet" - | "no_luggage" - | "airline_seat_legroom_extra" - | "airline_seat_flat_angled" - | "airline_seat_legroom_normal" - | "airline_seat_legroom_reduced" - | "no_stroller" - | "travel" - | "your_trips" - | "house" - | "bed" - | "ac_unit" - | "chair" - | "coffee" - | "electric_bolt" - | "child_care" - | "sensors" - | "back_hand" - | "checkroom" - | "emergency_home" - | "grass" - | "shower" - | "mode_fan" - | "mop" - | "kitchen" - | "room_service" - | "thermometer" - | "styler" - | "yard" - | "bathtub" - | "king_bed" - | "roofing" - | "energy_savings_leaf" - | "window" - | "valve" - | "cooking" - | "garage_home" - | "door_front" - | "mode_heat" - | "light" - | "foundation" - | "outdoor_grill" - | "garage" - | "dining" - | "table_restaurant" - | "sensor_occupied" - | "deck" - | "weekend" - | "coffee_maker" - | "humidity_high" - | "flatware" - | "highlight" - | "fireplace" - | "humidity_low" - | "mode_night" - | "electric_meter" - | "tv_gen" - | "humidity_mid" - | "bedroom_parent" - | "chair_alt" - | "blender" - | "scene" - | "microwave" - | "oven_gen" - | "single_bed" - | "bedroom_baby" - | "heat_pump" - | "bathroom" - | "in_home_mode" - | "hot_tub" - | "hardware" - | "mode_off_on" - | "sprinkler" - | "table_bar" - | "gas_meter" - | "crib" - | "soap" - | "countertops" - | "living" - | "mode_cool" - | "home_iot_device" - | "propane_tank" - | "fire_extinguisher" - | "outlet" - | "remote_gen" - | "matter" - | "gate" - | "sensor_door" - | "event_seat" - | "airware" - | "faucet" - | "dishwasher_gen" - | "energy_program_saving" - | "air_freshener" - | "balcony" - | "wash" - | "camera_indoor" - | "water_damage" - | "bedroom_child" - | "house_siding" - | "switch" - | "microwave_gen" - | "detector_smoke" - | "door_sliding" - | "iron" - | "desk" - | "energy_program_time_used" - | "umbrella" - | "water_heater" - | "dresser" - | "fence" - | "door_back" - | "doorbell" - | "mode_fan_off" - | "hvac" - | "kettle" - | "camera_outdoor" - | "emergency_heat" - | "air_purifier_gen" - | "emergency_share" - | "stroller" - | "curtains" - | "multicooker" - | "shield_moon" - | "sensors_off" - | "mode_heat_cool" - | "thermostat_auto" - | "emergency_recording" - | "smart_outlet" - | "blinds" - | "controller_gen" - | "roller_shades" - | "dry" - | "blinds_closed" - | "roller_shades_closed" - | "propane" - | "sensor_window" - | "thermostat_carbon" - | "range_hood" - | "doorbell_3p" - | "blanket" - | "tv_with_assistant" - | "vertical_shades_closed" - | "vertical_shades" - | "curtains_closed" - | "mode_heat_off" - | "mode_cool_off" - | "tamper_detection_off" - | "shelves" - | "thermometer_gain" - | "wall_art" - | "thermometer_loss" - | "hallway" - | "emergency_share_off" - | "stadia_controller" - | "door_open" - | "nest_eco_leaf" - | "device_reset" - | "nest_clock_farsight_analog" - | "nest_remote_comfort_sensor" - | "laundry" - | "battery_horiz_075" - | "shield_with_heart" - | "temp_preferences_eco" - | "familiar_face_and_zone" - | "tools_power_drill" - | "airwave" - | "productivity" - | "battery_horiz_050" - | "nest_heat_link_gen_3" - | "nest_display" - | "weather_snowy" - | "activity_zone" - | "ev_charger" - | "nest_remote" - | "cleaning_bucket" - | "settings_alert" - | "nest_cam_indoor" - | "arrows_more_up" - | "nest_heat_link_e" - | "home_storage" - | "nest_multi_room" - | "nest_secure_alarm" - | "battery_horiz_000" - | "light_group" - | "google_wifi" - | "nest_cam_outdoor" - | "detection_and_zone" - | "nest_thermostat_gen_3" - | "mfg_nest_yale_lock" - | "tools_pliers_wire_stripper" - | "detector_alarm" - | "nest_cam_iq_outdoor" - | "tools_ladder" - | "early_on" - | "floor_lamp" - | "nest_clock_farsight_digital" - | "nest_cam_iq" - | "home_speaker" - | "nest_mini" - | "nest_hello_doorbell" - | "home_max_dots" - | "nest_audio" - | "nest_wifi_router" - | "house_with_shield" - | "zone_person_urgent" - | "nest_display_max" - | "motion_sensor_active" - | "cool_to_dry" - | "shield_with_house" - | "nest_farsight_weather" - | "chromecast_2" - | "battery_profile" - | "window_closed" - | "heat_pump_balance" - | "arming_countdown" - | "nest_found_savings" - | "detector_status" - | "self_care" - | "tools_level" - | "window_open" - | "nest_thermostat_zirconium_eu" - | "arrows_more_down" - | "nest_true_radiant" - | "nest_cam_wired_stand" - | "zone_person_alert" - | "detector" - | "climate_mini_split" - | "nest_detect" - | "nest_wifi_point" - | "door_sensor" - | "nest_doorbell_visitor" - | "quiet_time" - | "nest_cam_floodlight" - | "nest_tag" - | "tools_installation_kit" - | "nest_connect" - | "nest_thermostat_sensor_eu" - | "nest_sunblock" - | "tools_phillips" - | "nest_thermostat_sensor" - | "nest_wifi_gale" - | "nest_thermostat_e_eu" - | "doorbell_chime" - | "detector_co" - | "detector_battery" - | "tools_flat_head" - | "nest_wake_on_approach" - | "nest_wake_on_press" - | "motion_sensor_urgent" - | "table_lamp" - | "motion_sensor_alert" - | "window_sensor" - | "tamper_detection_on" - | "nest_cam_magnet_mount" - | "zone_person_idle" - | "quiet_time_active" - | "nest_cam_stand" - | "detector_offline" - | "wall_lamp" - | "nest_cam_wall_mount" - | "motion_sensor_idle" - | "nest_thermostat" - | "water_pump" - | "assistant_on_hub" - | "nest_protect" - | "google_tv_remote" - | "feedback" - | "flutter"; diff --git a/web/components/views/index.ts b/web/components/views/index.ts index c2e8cd6de..b7ebe5081 100644 --- a/web/components/views/index.ts +++ b/web/components/views/index.ts @@ -3,4 +3,3 @@ export * from "./form"; export * from "./modal"; export * from "./view-list-item"; export * from "./views-list"; -export * from "./workspace-dashboard"; diff --git a/web/hooks/use-editor-suggestions.tsx b/web/hooks/use-editor-suggestions.tsx index 6247989f0..337fc5f0a 100644 --- a/web/hooks/use-editor-suggestions.tsx +++ b/web/hooks/use-editor-suggestions.tsx @@ -1,9 +1,7 @@ -import { IMentionHighlight, IMentionSuggestion } from "@plane/rich-text-editor"; -import useUser from "./use-user"; import { useMobxStore } from "lib/mobx/store-provider"; import { RootStore } from "store/root"; -const useEditorSuggestions = (_workspaceSlug: string | undefined, _projectId: string | undefined) => { +const useEditorSuggestions = () => { const { mentionsStore }: RootStore = useMobxStore(); return { diff --git a/web/package.json b/web/package.json index c4513a555..b2791978f 100644 --- a/web/package.json +++ b/web/package.json @@ -11,14 +11,10 @@ "export": "next export" }, "dependencies": { - "@blueprintjs/core": "^4.16.3", "@blueprintjs/popover2": "^1.13.3", - "@emotion/react": "^11.11.1", - "@emotion/styled": "^11.11.0", "@headlessui/react": "^1.7.3", "@hello-pangea/dnd": "^16.3.0", "@jitsu/nextjs": "^3.1.5", - "@mui/material": "^5.14.1", "@nivo/bar": "0.80.0", "@nivo/calendar": "0.80.0", "@nivo/core": "0.80.0", @@ -34,13 +30,10 @@ "@types/lodash.debounce": "^4.0.7", "@types/react-datepicker": "^4.8.0", "axios": "^1.1.3", - "clsx": "^2.0.0", "cmdk": "^0.2.0", "dotenv": "^16.0.3", - "highlight.js": "^11.8.0", "js-cookie": "^3.0.1", "lodash.debounce": "^4.0.8", - "lowlight": "^2.9.0", "lucide-react": "^0.274.0", "mobx": "^6.10.0", "mobx-react-lite": "^4.0.3", @@ -49,29 +42,24 @@ "next-themes": "^0.2.1", "nprogress": "^0.2.0", "react": "18.2.0", - "react-beautiful-dnd": "^13.1.1", "react-color": "^2.19.3", "react-datepicker": "^4.8.0", "react-dom": "18.2.0", "react-dropzone": "^14.2.3", "react-hook-form": "^7.38.0", "react-markdown": "^8.0.7", - "react-moveable": "^0.54.1", "react-popper": "^2.3.0", "sharp": "^0.32.1", "swr": "^2.1.3", - "tailwind-merge": "^1.14.0", - "tlds": "^1.238.0", "uuid": "^9.0.0" }, "devDependencies": { "@types/js-cookie": "^3.0.2", "@types/node": "18.0.6", "@types/nprogress": "^0.2.0", - "@types/react": "18.0.15", - "@types/react-beautiful-dnd": "^13.1.2", + "@types/react": "^18.2.35", "@types/react-color": "^3.0.6", - "@types/react-dom": "18.0.6", + "@types/react-dom": "^18.2.14", "@types/uuid": "^8.3.4", "@typescript-eslint/eslint-plugin": "^5.48.2", "@typescript-eslint/parser": "^5.48.2", diff --git a/web/pages/[workspaceSlug]/index.tsx b/web/pages/[workspaceSlug]/index.tsx index 703882a23..daeb8aae5 100644 --- a/web/pages/[workspaceSlug]/index.tsx +++ b/web/pages/[workspaceSlug]/index.tsx @@ -2,7 +2,7 @@ import { ReactElement } from "react"; // layouts import { AppLayout } from "layouts/app-layout"; // components -import { WorkspaceDashboardView } from "components/views"; +import { WorkspaceDashboardView } from "components/page-views"; import { WorkspaceDashboardHeader } from "components/headers/workspace-dashboard"; // types import { NextPageWithLayout } from "types/app"; diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx index 775cfcd11..318d61cf5 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx @@ -1,19 +1,14 @@ -import { useState, ReactElement } from "react"; +import { ReactElement } from "react"; import { useRouter } from "next/router"; import useSWR from "swr"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; -// services -import { IssueService } from "services/issue"; // hooks import useLocalStorage from "hooks/use-local-storage"; -import useUser from "hooks/use-user"; -import useToast from "hooks/use-toast"; // layouts import { AppLayout } from "layouts/app-layout"; // components import { CycleIssuesHeader } from "components/headers"; -import { ExistingIssuesListModal } from "components/core"; import { CycleDetailsSidebar } from "components/cycles"; import { CycleLayoutRoot } from "components/issues/issue-layouts"; // ui @@ -21,23 +16,14 @@ import { EmptyState } from "components/common"; // assets import emptyCycle from "public/empty-state/cycle.svg"; // types -import { ISearchIssueResponse } from "types"; import { NextPageWithLayout } from "types/app"; -const issueService = new IssueService(); - const CycleDetailPage: NextPageWithLayout = () => { - const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false); - const router = useRouter(); const { workspaceSlug, projectId, cycleId } = router.query; const { cycle: cycleStore } = useMobxStore(); - const { user } = useUser(); - - const { setToastAlert } = useToast(); - const { setValue, storedValue } = useLocalStorage("cycle_sidebar_collapsed", "false"); const isSidebarCollapsed = storedValue ? (storedValue === "true" ? true : false) : false; @@ -52,38 +38,8 @@ const CycleDetailPage: NextPageWithLayout = () => { setValue(`${!isSidebarCollapsed}`); }; - // TODO: add this function to bulk add issues to cycle - const handleAddIssuesToCycle = async (data: ISearchIssueResponse[]) => { - if (!workspaceSlug || !projectId) return; - - const payload = { - issues: data.map((i) => i.id), - }; - - await issueService - .addIssueToCycle(workspaceSlug as string, projectId as string, cycleId as string, payload, user) - .catch(() => { - setToastAlert({ - type: "error", - title: "Error!", - message: "Selected issues could not be added to the cycle. Please try again.", - }); - }); - }; - - const openIssuesListModal = () => { - setCycleIssuesListModal(true); - }; - return ( <> - {/* TODO: Update logic to bulk add issues to a cycle */} - setCycleIssuesListModal(false)} - searchParams={{ cycle: true }} - handleOnSubmit={handleAddIssuesToCycle} - /> {error ? ( { <>
- +
{cycleId && !isSidebarCollapsed && (
{ - // states - const [moduleIssuesListModal, setModuleIssuesListModal] = useState(false); // router const router = useRouter(); const { workspaceSlug, projectId, moduleId } = router.query; // store const { module: moduleStore } = useMobxStore(); - // hooks - const { user } = useUser(); - const { setToastAlert } = useToast(); // local storage const { setValue, storedValue } = useLocalStorage("module_sidebar_collapsed", "false"); const isSidebarCollapsed = storedValue ? (storedValue === "true" ? true : false) : false; @@ -48,42 +35,12 @@ const ModuleIssuesPage: NextPageWithLayout = () => { : null ); - // TODO: add this function to bulk add issues to cycle - const handleAddIssuesToModule = async (data: ISearchIssueResponse[]) => { - if (!workspaceSlug || !projectId) return; - - const payload = { - issues: data.map((i) => i.id), - }; - - await moduleService - .addIssuesToModule(workspaceSlug as string, projectId as string, moduleId as string, payload, user) - .catch(() => - setToastAlert({ - type: "error", - title: "Error!", - message: "Selected issues could not be added to the module. Please try again.", - }) - ); - }; - - const openIssuesListModal = () => { - setModuleIssuesListModal(true); - }; - const toggleSidebar = () => { setValue(`${!isSidebarCollapsed}`); }; return ( <> - {/* TODO: Update logic to bulk add issues to a cycle */} - setModuleIssuesListModal(false)} - searchParams={{ module: true }} - handleOnSubmit={handleAddIssuesToModule} - /> {error ? ( { ) : (
- +
{moduleId && !isSidebarCollapsed && (
{ const router = useRouter(); - const { installation_id, setup_action, state, provider, code } = router.query; + const { installation_id, state, provider, code } = router.query; useEffect(() => { if (provider === "github" && state && installation_id) { @@ -27,53 +27,37 @@ const AppPostInstallation: NextPageWithLayout = () => { console.log(err); }); } else if (provider === "slack" && state && code) { - appInstallationService - .getSlackAuthDetails(code.toString()) - .then((res) => { - const [workspaceSlug, projectId, integrationId] = state.toString().split(","); + const [workspaceSlug, projectId, integrationId] = state.toString().split(","); - if (!projectId) { - const payload = { - metadata: { - ...res, - }, - }; - - appInstallationService - .addInstallationApp(state.toString(), provider, payload) - .then((r) => { - window.opener = null; - window.open("", "_self"); - window.close(); - }) - .catch((err) => { - throw err?.response; - }); - } else { - const payload = { - access_token: res.access_token, - bot_user_id: res.bot_user_id, - webhook_url: res.incoming_webhook.url, - data: res, - team_id: res.team.id, - team_name: res.team.name, - scopes: res.scope, - }; - appInstallationService - .addSlackChannel(workspaceSlug, projectId, integrationId, payload) - .then((r) => { - window.opener = null; - window.open("", "_self"); - window.close(); - }) - .catch((err) => { - throw err.response; - }); - } - }) - .catch((err) => { - console.log(err); - }); + if (!projectId) { + const payload = { + code, + }; + appInstallationService + .addInstallationApp(state.toString(), provider, payload) + .then(() => { + window.opener = null; + window.open("", "_self"); + window.close(); + }) + .catch((err) => { + throw err?.response; + }); + } else { + const payload = { + code, + }; + appInstallationService + .addSlackChannel(workspaceSlug, projectId, integrationId, payload) + .then(() => { + window.opener = null; + window.open("", "_self"); + window.close(); + }) + .catch((err) => { + throw err.response; + }); + } } }, [state, installation_id, provider, code]); diff --git a/web/services/app_installation.service.ts b/web/services/app_installation.service.ts index 2a7a4ea6a..179721036 100644 --- a/web/services/app_installation.service.ts +++ b/web/services/app_installation.service.ts @@ -60,16 +60,4 @@ export class AppInstallationService extends APIService { throw error?.response; }); } - - async getSlackAuthDetails(code: string): Promise { - const response = await this.request({ - method: "post", - url: "/api/slack-redirect", - data: { - code, - }, - }); - - return response.data; - } } diff --git a/web/store/cycle/cycle_issue.store.ts b/web/store/cycle/cycle_issue.store.ts index d5d615203..7028e1120 100644 --- a/web/store/cycle/cycle_issue.store.ts +++ b/web/store/cycle/cycle_issue.store.ts @@ -36,7 +36,7 @@ export interface ICycleIssueStore { updateIssueStructure: (group_id: string | null, sub_group_id: string | null, issue: IIssue) => void; updateGanttIssueStructure: (workspaceSlug: string, cycleId: string, issue: IIssue, payload: IBlockUpdateData) => void; deleteIssue: (group_id: string | null, sub_group_id: string | null, issue: IIssue) => void; - addIssueToCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => void; + addIssueToCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => Promise; removeIssueFromCycle: (workspaceSlug: string, projectId: string, cycleId: string, bridgeId: string) => void; } @@ -327,7 +327,7 @@ export class CycleIssueStore implements ICycleIssueStore { } }; - addIssueToCycle = async (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => { + addIssueToCycle = async (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => { try { const user = this.rootStore.user.currentUser ?? undefined; @@ -336,7 +336,7 @@ export class CycleIssueStore implements ICycleIssueStore { projectId, cycleId, { - issues: [issueId], + issues: issueIds, }, user ); diff --git a/web/store/cycle/cycle_issue_calendar_view.store.ts b/web/store/cycle/cycle_issue_calendar_view.store.ts index fa80f39ac..3b695d697 100644 --- a/web/store/cycle/cycle_issue_calendar_view.store.ts +++ b/web/store/cycle/cycle_issue_calendar_view.store.ts @@ -36,8 +36,8 @@ export class CycleIssueCalendarViewStore implements ICycleIssueCalendarViewStore projectId: projectId, }; - const droppableSourceColumnId = source.droppableId; - const droppableDestinationColumnId = destination.droppableId; + const droppableSourceColumnId = source?.droppableId || null; + const droppableDestinationColumnId = destination?.droppableId || null; if (droppableSourceColumnId === droppableDestinationColumnId) return; diff --git a/web/store/cycle/cycle_issue_kanban_view.store.ts b/web/store/cycle/cycle_issue_kanban_view.store.ts index 0ffbd883b..0ecc96e60 100644 --- a/web/store/cycle/cycle_issue_kanban_view.store.ts +++ b/web/store/cycle/cycle_issue_kanban_view.store.ts @@ -44,6 +44,7 @@ export class CycleIssueKanBanViewStore implements ICycleIssueKanBanViewStore { } get canUserDragDrop() { + if (this.rootStore.issueDetail.peekId) return false; if ( this.rootStore?.issueFilter?.userDisplayFilters?.order_by && this.rootStore?.issueFilter?.userDisplayFilters?.order_by === "sort_order" && @@ -94,9 +95,9 @@ export class CycleIssueKanBanViewStore implements ICycleIssueKanBanViewStore { }; // source, destination group and sub group id - let droppableSourceColumnId = source.droppableId; + let droppableSourceColumnId = source?.droppableId || null; droppableSourceColumnId = droppableSourceColumnId ? droppableSourceColumnId.split("__") : null; - let droppableDestinationColumnId = destination.droppableId; + let droppableDestinationColumnId = destination?.droppableId || null; droppableDestinationColumnId = droppableDestinationColumnId ? droppableDestinationColumnId.split("__") : null; if (!droppableSourceColumnId || !droppableDestinationColumnId) return null; @@ -314,9 +315,9 @@ export class CycleIssueKanBanViewStore implements ICycleIssueKanBanViewStore { }; // source, destination group and sub group id - let droppableSourceColumnId = source.droppableId; + let droppableSourceColumnId = source?.droppableId || null; droppableSourceColumnId = droppableSourceColumnId ? droppableSourceColumnId.split("__") : null; - let droppableDestinationColumnId = destination.droppableId; + let droppableDestinationColumnId = destination?.droppableId || null; droppableDestinationColumnId = droppableDestinationColumnId ? droppableDestinationColumnId.split("__") : null; if (!droppableSourceColumnId || !droppableDestinationColumnId) return null; diff --git a/web/store/issue/issue_calendar_view.store.ts b/web/store/issue/issue_calendar_view.store.ts index 5f9cc89bf..881e2ee83 100644 --- a/web/store/issue/issue_calendar_view.store.ts +++ b/web/store/issue/issue_calendar_view.store.ts @@ -35,8 +35,8 @@ export class IssueCalendarViewStore implements IIssueCalendarViewStore { projectId: projectId, }; - const droppableSourceColumnId = source.droppableId; - const droppableDestinationColumnId = destination.droppableId; + const droppableSourceColumnId = source?.droppableId || null; + const droppableDestinationColumnId = destination?.droppableId || null; if (droppableSourceColumnId === droppableDestinationColumnId) return; diff --git a/web/store/issue/issue_filters.store.ts b/web/store/issue/issue_filters.store.ts index f6e84c622..be28cca87 100644 --- a/web/store/issue/issue_filters.store.ts +++ b/web/store/issue/issue_filters.store.ts @@ -17,6 +17,7 @@ import { export interface IIssueFilterStore { loader: boolean; error: any | null; + // TODO: store filters and properties separately for each project userDisplayProperties: IIssueDisplayProperties; userDisplayFilters: IIssueDisplayFilterOptions; userFilters: IIssueFilterOptions; diff --git a/web/store/issue/issue_kanban_view.store.ts b/web/store/issue/issue_kanban_view.store.ts index 9faf3190c..827972694 100644 --- a/web/store/issue/issue_kanban_view.store.ts +++ b/web/store/issue/issue_kanban_view.store.ts @@ -44,6 +44,7 @@ export class IssueKanBanViewStore implements IIssueKanBanViewStore { } get canUserDragDrop() { + if (this.rootStore.issueDetail.peekId) return false; if ( this.rootStore?.issueFilter?.userDisplayFilters?.order_by && this.rootStore?.issueFilter?.userDisplayFilters?.order_by === "sort_order" && @@ -94,9 +95,9 @@ export class IssueKanBanViewStore implements IIssueKanBanViewStore { }; // source, destination group and sub group id - let droppableSourceColumnId = source.droppableId; + let droppableSourceColumnId = source?.droppableId || null; droppableSourceColumnId = droppableSourceColumnId ? droppableSourceColumnId.split("__") : null; - let droppableDestinationColumnId = destination.droppableId; + let droppableDestinationColumnId = destination?.droppableId || null; droppableDestinationColumnId = droppableDestinationColumnId ? droppableDestinationColumnId.split("__") : null; if (!droppableSourceColumnId || !droppableDestinationColumnId) return null; @@ -314,9 +315,9 @@ export class IssueKanBanViewStore implements IIssueKanBanViewStore { }; // source, destination group and sub group id - let droppableSourceColumnId = source.droppableId; + let droppableSourceColumnId = source?.droppableId || null; droppableSourceColumnId = droppableSourceColumnId ? droppableSourceColumnId.split("__") : null; - let droppableDestinationColumnId = destination.droppableId; + let droppableDestinationColumnId = destination?.droppableId || null; droppableDestinationColumnId = droppableDestinationColumnId ? droppableDestinationColumnId.split("__") : null; if (!droppableSourceColumnId || !droppableDestinationColumnId) return null; diff --git a/web/store/module/module_issue.store.ts b/web/store/module/module_issue.store.ts index a8abfe2e7..b92ee27d0 100644 --- a/web/store/module/module_issue.store.ts +++ b/web/store/module/module_issue.store.ts @@ -40,7 +40,7 @@ export interface IModuleIssueStore { payload: IBlockUpdateData ) => void; deleteIssue: (group_id: string | null, sub_group_id: string | null, issue: IIssue) => void; - addIssueToModule: (workspaceSlug: string, projectId: string, moduleId: string, issueId: string) => Promise; + addIssueToModule: (workspaceSlug: string, projectId: string, moduleId: string, issueIds: string[]) => Promise; removeIssueFromModule: (workspaceSlug: string, projectId: string, moduleId: string, bridgeId: string) => Promise; } @@ -337,7 +337,7 @@ export class ModuleIssueStore implements IModuleIssueStore { } }; - addIssueToModule = async (workspaceSlug: string, projectId: string, moduleId: string, issueId: string) => { + addIssueToModule = async (workspaceSlug: string, projectId: string, moduleId: string, issueIds: string[]) => { try { const user = this.rootStore.user.currentUser ?? undefined; @@ -346,7 +346,7 @@ export class ModuleIssueStore implements IModuleIssueStore { projectId, moduleId, { - issues: [issueId], + issues: issueIds, }, user ); diff --git a/web/store/module/module_issue_calendar_view.store.ts b/web/store/module/module_issue_calendar_view.store.ts index 313745a18..3bfed3140 100644 --- a/web/store/module/module_issue_calendar_view.store.ts +++ b/web/store/module/module_issue_calendar_view.store.ts @@ -36,8 +36,8 @@ export class ModuleIssueCalendarViewStore implements IModuleIssueCalendarViewSto projectId: projectId, }; - const droppableSourceColumnId = source.droppableId; - const droppableDestinationColumnId = destination.droppableId; + const droppableSourceColumnId = source?.droppableId || null; + const droppableDestinationColumnId = destination?.droppableId || null; if (droppableSourceColumnId === droppableDestinationColumnId) return; diff --git a/web/store/module/module_issue_kanban_view.store.ts b/web/store/module/module_issue_kanban_view.store.ts index d06fbf02b..82e210f29 100644 --- a/web/store/module/module_issue_kanban_view.store.ts +++ b/web/store/module/module_issue_kanban_view.store.ts @@ -44,6 +44,7 @@ export class ModuleIssueKanBanViewStore implements IModuleIssueKanBanViewStore { } get canUserDragDrop() { + if (this.rootStore.issueDetail.peekId) return false; if ( this.rootStore?.issueFilter?.userDisplayFilters?.order_by && this.rootStore?.issueFilter?.userDisplayFilters?.order_by === "sort_order" && @@ -94,9 +95,9 @@ export class ModuleIssueKanBanViewStore implements IModuleIssueKanBanViewStore { }; // source, destination group and sub group id - let droppableSourceColumnId = source.droppableId; + let droppableSourceColumnId = source?.droppableId || null; droppableSourceColumnId = droppableSourceColumnId ? droppableSourceColumnId.split("__") : null; - let droppableDestinationColumnId = destination.droppableId; + let droppableDestinationColumnId = destination?.droppableId || null; droppableDestinationColumnId = droppableDestinationColumnId ? droppableDestinationColumnId.split("__") : null; if (!droppableSourceColumnId || !droppableDestinationColumnId) return null; @@ -314,9 +315,9 @@ export class ModuleIssueKanBanViewStore implements IModuleIssueKanBanViewStore { }; // source, destination group and sub group id - let droppableSourceColumnId = source.droppableId; + let droppableSourceColumnId = source?.droppableId || null; droppableSourceColumnId = droppableSourceColumnId ? droppableSourceColumnId.split("__") : null; - let droppableDestinationColumnId = destination.droppableId; + let droppableDestinationColumnId = destination?.droppableId || null; droppableDestinationColumnId = droppableDestinationColumnId ? droppableDestinationColumnId.split("__") : null; if (!droppableSourceColumnId || !droppableDestinationColumnId) return null; diff --git a/web/store/project-view/project_view_issue_calendar_view.store.ts b/web/store/project-view/project_view_issue_calendar_view.store.ts index 2f70df136..9bce218ae 100644 --- a/web/store/project-view/project_view_issue_calendar_view.store.ts +++ b/web/store/project-view/project_view_issue_calendar_view.store.ts @@ -36,8 +36,8 @@ export class ProjectViewIssueCalendarViewStore implements IProjectViewIssueCalen projectId: projectId, }; - const droppableSourceColumnId = source.droppableId; - const droppableDestinationColumnId = destination.droppableId; + const droppableSourceColumnId = source?.droppableId || null; + const droppableDestinationColumnId = destination?.droppableId || null; if (droppableSourceColumnId === droppableDestinationColumnId) return; diff --git a/web/styles/globals.css b/web/styles/globals.css index e30425936..cde7993a1 100644 --- a/web/styles/globals.css +++ b/web/styles/globals.css @@ -211,9 +211,9 @@ --color-sidebar-text-400: var(--color-text-400); /* sidebar placeholder text */ --color-sidebar-border-100: var(--color-border-100); /* subtle sidebar border= 1 */ - --color-sidebar-border-200: var(--color-border-100); /* subtle sidebar border- 2 */ - --color-sidebar-border-300: var(--color-border-100); /* strong sidebar border- 1 */ - --color-sidebar-border-400: var(--color-border-100); /* strong sidebar border- 2 */ + --color-sidebar-border-200: var(--color-border-200); /* subtle sidebar border- 2 */ + --color-sidebar-border-300: var(--color-border-300); /* strong sidebar border- 1 */ + --color-sidebar-border-400: var(--color-border-400); /* strong sidebar border- 2 */ } } diff --git a/yarn.lock b/yarn.lock index 678a9e7a5..f25d1cf7d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -919,7 +919,7 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.1.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.15.4", "@babel/runtime@^7.18.3", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.1.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.18.3", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.23.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885" integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg== @@ -1343,10 +1343,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/eslintrc@^2.0.1", "@eslint/eslintrc@^2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.2.tgz#c6936b4b328c64496692f76944e755738be62396" - integrity sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g== +"@eslint/eslintrc@^2.0.1", "@eslint/eslintrc@^2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.3.tgz#797470a75fe0fbd5a53350ee715e85e87baff22d" + integrity sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA== dependencies: ajv "^6.12.4" debug "^4.3.2" @@ -1363,10 +1363,10 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.36.0.tgz#9837f768c03a1e4a30bd304a64fb8844f0e72efe" integrity sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg== -"@eslint/js@8.52.0": - version "8.52.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.52.0.tgz#78fe5f117840f69dc4a353adf9b9cd926353378c" - integrity sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA== +"@eslint/js@8.53.0": + version "8.53.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.53.0.tgz#bea56f2ed2b5baea164348ff4d5a879f6f81f20d" + integrity sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w== "@floating-ui/core@^1.4.2": version "1.5.0" @@ -2641,7 +2641,7 @@ dependencies: "@types/unist" "*" -"@types/hoist-non-react-statics@^3.3.0", "@types/hoist-non-react-statics@^3.3.1": +"@types/hoist-non-react-statics@^3.3.1": version "3.3.4" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.4.tgz#cc477ce0283bb9d19ea0cbfa2941fe2c8493a1be" integrity sha512-ZchYkbieA+7tnxwX/SCBySx9WwvWR8TaP5tb2jRAzwvLb/rWchGw3v0w3pqUbUvj0GCwW2Xz/AVPSk6kUGctXQ== @@ -2758,13 +2758,6 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.9.tgz#b6f785caa7ea1fe4414d9df42ee0ab67f23d8a6d" integrity sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g== -"@types/react-beautiful-dnd@^13.1.2": - version "13.1.6" - resolved "https://registry.yarnpkg.com/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.6.tgz#a616443903bfc370fee298b0144dbce7234d5561" - integrity sha512-FXAuaa52ux7HWQDumi3MFSAAsW8OKfDImy1pSZPKe85nV9mZ1f4spVzW0a2boYvkIhphjbWUi5EwUiRG8Rq/Qg== - dependencies: - "@types/react" "*" - "@types/react-color@^3.0.6", "@types/react-color@^3.0.9": version "3.0.9" resolved "https://registry.yarnpkg.com/@types/react-color/-/react-color-3.0.9.tgz#8dbb0d798f2979c3d7e2e26dd46321e80da950b4" @@ -2790,13 +2783,6 @@ dependencies: "@types/react" "*" -"@types/react-dom@18.0.6": - version "18.0.6" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.6.tgz#36652900024842b74607a17786b6662dd1e103a1" - integrity sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA== - dependencies: - "@types/react" "*" - "@types/react-dom@18.2.0": version "18.2.0" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.0.tgz#374f28074bb117f56f58c4f3f71753bebb545156" @@ -2804,15 +2790,12 @@ dependencies: "@types/react" "*" -"@types/react-redux@^7.1.20": - version "7.1.28" - resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.28.tgz#30a44303c7daceb6ede9cfb4aaf72e64f1dde4de" - integrity sha512-EQr7cChVzVUuqbA+J8ArWK1H0hLAHKOs21SIMrskKZ3nHNeE+LFYA+IsoZGhVOT8Ktjn3M20v4rnZKN3fLbypw== +"@types/react-dom@^18.2.14": + version "18.2.14" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.14.tgz#c01ba40e5bb57fc1dc41569bb3ccdb19eab1c539" + integrity sha512-V835xgdSVmyQmI1KLV2BEIUgqEuinxp9O4G6g3FqO/SqLac049E53aysv0oEFD2kHfejeKU+ZqL2bcFWj9gLAQ== dependencies: - "@types/hoist-non-react-statics" "^3.3.0" "@types/react" "*" - hoist-non-react-statics "^3.3.0" - redux "^4.0.0" "@types/react-transition-group@^4.4.8": version "4.4.8" @@ -2821,7 +2804,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@18.0.15", "@types/react@18.0.28", "@types/react@18.2.0", "@types/react@^18.2.5": +"@types/react@*", "@types/react@18.0.28", "@types/react@18.2.0", "@types/react@^18.2.35", "@types/react@^18.2.5": version "18.2.0" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.0.tgz#15cda145354accfc09a18d2f2305f9fc099ada21" integrity sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA== @@ -3195,9 +3178,9 @@ astral-regex@^2.0.0: integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== async@^3.2.3: - version "3.2.4" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" - integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + version "3.2.5" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== asynciterator.prototype@^1.0.0: version "1.0.0" @@ -3438,9 +3421,9 @@ camelcase-css@^2.0.1: integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001538, caniuse-lite@^1.0.30001541: - version "1.0.30001559" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001559.tgz#95a982440d3d314c471db68d02664fb7536c5a30" - integrity sha512-cPiMKZgqgkg5LY3/ntGeLFUpi6tzddBNS58A4tnTgQw1zON7u2sZMU7SzOeVH4tj20++9ggL+V6FDOFMTaFFYA== + version "1.0.30001561" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz#752f21f56f96f1b1a52e97aae98c57c562d5d9da" + integrity sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw== capital-case@^1.0.4: version "1.0.4" @@ -3709,7 +3692,7 @@ crypto-random-string@^2.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== -css-box-model@^1.2.0, css-box-model@^1.2.1: +css-box-model@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.1.tgz#59951d3b81fd6b2074a62d49444415b0d2b4d7c1" integrity sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw== @@ -4037,9 +4020,9 @@ ejs@^3.1.6: jake "^10.8.5" electron-to-chromium@^1.4.535: - version "1.4.575" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.575.tgz#7c0b87eb2c6214a993699792abd704de41255c39" - integrity sha512-kY2BGyvgAHiX899oF6xLXSIf99bAvvdPhDoJwG77nxCSyWYuRH6e9a9a3gpXBvCs6lj4dQZJkfnW2hdKWHEISg== + version "1.4.576" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.576.tgz#0c6940fdc0d60f7e34bd742b29d8fa847c9294d1" + integrity sha512-yXsZyXJfAqzWk1WKryr0Wl0MN2D47xodPvEEwlVePBnhU5E7raevLQR+E6b9JAD3GfL/7MbAL9ZtWQQPcLx7wA== emoji-regex@^8.0.0: version "8.0.0" @@ -4745,14 +4728,14 @@ eslint@^7.23.0, eslint@^7.32.0: v8-compile-cache "^2.0.3" eslint@^8.31.0: - version "8.52.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.52.0.tgz#d0cd4a1fac06427a61ef9242b9353f36ea7062fc" - integrity sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg== + version "8.53.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.53.0.tgz#14f2c8244298fcae1f46945459577413ba2697ce" + integrity sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.6.1" - "@eslint/eslintrc" "^2.1.2" - "@eslint/js" "8.52.0" + "@eslint/eslintrc" "^2.1.3" + "@eslint/js" "8.53.0" "@humanwhocodes/config-array" "^0.11.13" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" @@ -6208,11 +6191,6 @@ mdurl@^1.0.1: resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== -memoize-one@^5.1.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" - integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== - memoize-one@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045" @@ -6520,9 +6498,9 @@ mz@^2.7.0: thenify-all "^1.0.0" nanoid@^3.3.4, nanoid@^3.3.6: - version "3.3.6" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" - integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== napi-build-utils@^1.0.1: version "1.0.2" @@ -7269,7 +7247,7 @@ queue-tick@^1.0.1: resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142" integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag== -raf-schd@^4.0.2, raf-schd@^4.0.3: +raf-schd@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.3.tgz#5d6c34ef46f8b2a0e880a8fcdb743efc5bfdbc1a" integrity sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ== @@ -7291,19 +7269,6 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-beautiful-dnd@^13.1.1: - version "13.1.1" - resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz#b0f3087a5840920abf8bb2325f1ffa46d8c4d0a2" - integrity sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ== - dependencies: - "@babel/runtime" "^7.9.2" - css-box-model "^1.2.0" - memoize-one "^5.1.1" - raf-schd "^4.0.2" - react-redux "^7.2.0" - redux "^4.0.4" - use-memo-one "^1.1.1" - react-color@^2.19.3: version "2.19.3" resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.19.3.tgz#ec6c6b4568312a3c6a18420ab0472e146aa5683d" @@ -7360,20 +7325,15 @@ react-fast-compare@^3.0.1: integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ== react-hook-form@^7.38.0: - version "7.47.0" - resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.47.0.tgz#a42f07266bd297ddf1f914f08f4b5f9783262f31" - integrity sha512-F/TroLjTICipmHeFlMrLtNLceO2xr1jU3CyiNla5zdwsGUGu2UOxxR4UyJgLlhMwLW/Wzp4cpJ7CPfgJIeKdSg== + version "7.48.2" + resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.48.2.tgz#01150354d2be61412ff56a030b62a119283b9935" + integrity sha512-H0T2InFQb1hX7qKtDIZmvpU1Xfn/bdahWBN1fH19gSe4bBEqTfmlr7H3XWTaVtiK4/tpPaI1F3355GPMZYge+A== react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-is@^17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== - react-is@^18.0.0, react-is@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" @@ -7405,7 +7365,7 @@ react-markdown@^8.0.7: unist-util-visit "^4.0.0" vfile "^5.0.0" -react-moveable@^0.54.1, react-moveable@^0.54.2: +react-moveable@^0.54.2: version "0.54.2" resolved "https://registry.yarnpkg.com/react-moveable/-/react-moveable-0.54.2.tgz#87ce9af3499dc1c8218bce7e174b10264c1bbecf" integrity sha512-NGaVLbn0i9pb3+BWSKGWFqI/Mgm4+WMeWHxXXQ4Qi1tHxWCXrUrbGvpxEpt69G/hR7dez+/m68ex+fabjnvcUg== @@ -7450,18 +7410,6 @@ react-popper@^2.2.5, react-popper@^2.3.0: react-fast-compare "^3.0.1" warning "^4.0.2" -react-redux@^7.2.0: - version "7.2.9" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.9.tgz#09488fbb9416a4efe3735b7235055442b042481d" - integrity sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ== - dependencies: - "@babel/runtime" "^7.15.4" - "@types/react-redux" "^7.1.20" - hoist-non-react-statics "^3.3.2" - loose-envify "^1.4.0" - prop-types "^15.7.2" - react-is "^17.0.2" - react-redux@^8.1.1: version "8.1.3" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.1.3.tgz#4fdc0462d0acb59af29a13c27ffef6f49ab4df46" @@ -7556,7 +7504,7 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -redux@^4.0.0, redux@^4.0.4, redux@^4.2.1: +redux@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197" integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w== @@ -8368,11 +8316,6 @@ tiptap-markdown@^0.8.2: markdown-it-task-lists "^2.1.1" prosemirror-markdown "^1.11.1" -tlds@^1.238.0: - version "1.245.0" - resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.245.0.tgz#afd4c0c33be7227f8f2d4578573fc922b839ba84" - integrity sha512-fbSQFQr8f41/e9q9IoKKVv7CUFvvE3TVJo7m0JQJZcf7jOOXRRf9DIP4Uf04aovYGKG686OvtV4ZrNY1bOz3aA== - to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -8785,7 +8728,7 @@ use-debounce@^9.0.4: resolved "https://registry.yarnpkg.com/use-debounce/-/use-debounce-9.0.4.tgz#51d25d856fbdfeb537553972ce3943b897f1ac85" integrity sha512-6X8H/mikbrt0XE8e+JXRtZ8yYVvKkdYRfmIhWZYsP8rcNs9hk3APV8Ua2mFkKRLcJKVdnX2/Vwrmg2GWKUQEaQ== -use-memo-one@^1.1.1, use-memo-one@^1.1.3: +use-memo-one@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.3.tgz#2fd2e43a2169eabc7496960ace8c79efef975e99" integrity sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==