From 3913cf571f375b1e51f3f66004a0b32454cf3b9c Mon Sep 17 00:00:00 2001 From: Kyle Lacy Date: Sat, 15 Apr 2023 11:36:23 -0700 Subject: [PATCH 01/43] Make SMTP port and TLS configurable --- apiserver/plane/settings/common.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apiserver/plane/settings/common.py b/apiserver/plane/settings/common.py index c144eeb0b..b3a6883bd 100644 --- a/apiserver/plane/settings/common.py +++ b/apiserver/plane/settings/common.py @@ -174,11 +174,11 @@ EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" # Host for sending e-mail. EMAIL_HOST = os.environ.get("EMAIL_HOST") # Port for sending e-mail. -EMAIL_PORT = 587 +EMAIL_PORT = int(os.environ.get("EMAIL_PORT", 587)) # Optional SMTP authentication information for EMAIL_HOST. EMAIL_HOST_USER = os.environ.get("EMAIL_HOST_USER") EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASSWORD") -EMAIL_USE_TLS = True +EMAIL_USE_TLS = os.environ.get("EMAIL_USE_TLS", "1") == "1" SIMPLE_JWT = { @@ -210,4 +210,4 @@ SIMPLE_JWT = { CELERY_TIMEZONE = TIME_ZONE CELERY_TASK_SERIALIZER = 'json' -CELERY_ACCEPT_CONTENT = ['application/json'] \ No newline at end of file +CELERY_ACCEPT_CONTENT = ['application/json'] From 6af54ebbe75b9491e203e7cd8efb67be56766351 Mon Sep 17 00:00:00 2001 From: Kyle Lacy Date: Sat, 15 Apr 2023 12:32:00 -0700 Subject: [PATCH 02/43] Fix typo in `.env.example --- apiserver/.env.example | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apiserver/.env.example b/apiserver/.env.example index 15056f072..2e1ee6fd1 100644 --- a/apiserver/.env.example +++ b/apiserver/.env.example @@ -3,11 +3,11 @@ DJANGO_SETTINGS_MODULE="plane.settings.production" DATABASE_URL=postgres://plane:xyzzyspoon@db:5432/plane # Cache REDIS_URL=redis://redis:6379/ -# SMPT +# SMTP EMAIL_HOST="" EMAIL_HOST_USER="" EMAIL_HOST_PASSWORD="" -# AWS +# AWS AWS_REGION="" AWS_ACCESS_KEY_ID="" AWS_SECRET_ACCESS_KEY="" @@ -21,4 +21,4 @@ DISABLE_COLLECTSTATIC=1 DOCKERIZED=1 # GPT Envs OPENAI_API_KEY=0 -GPT_ENGINE=0 \ No newline at end of file +GPT_ENGINE=0 From f757d8232b56b5d94ed853a6e6c00763905f8610 Mon Sep 17 00:00:00 2001 From: Kyle Lacy Date: Sat, 15 Apr 2023 12:32:54 -0700 Subject: [PATCH 03/43] Add setting to disable extra TLS config for Celery broker connection --- apiserver/plane/settings/production.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apiserver/plane/settings/production.py b/apiserver/plane/settings/production.py index 8f8453aff..a3f2ea79d 100644 --- a/apiserver/plane/settings/production.py +++ b/apiserver/plane/settings/production.py @@ -239,8 +239,12 @@ SLACK_BOT_TOKEN = os.environ.get("SLACK_BOT_TOKEN", False) LOGGER_BASE_URL = os.environ.get("LOGGER_BASE_URL", False) +broker_ssl = os.environ.get("REDIS_BROKER_SSL", "1") == "1" redis_url = os.environ.get("REDIS_URL") -broker_url = f"{redis_url}?ssl_cert_reqs={ssl.CERT_NONE.name}&ssl_ca_certs={certifi.where()}" +if broker_ssl: + broker_url = f"{redis_url}?ssl_cert_reqs={ssl.CERT_NONE.name}&ssl_ca_certs={certifi.where()}" +else: + broker_url = redis_url CELERY_RESULT_BACKEND = broker_url -CELERY_BROKER_URL = broker_url \ No newline at end of file +CELERY_BROKER_URL = broker_url From e2294f91058e14b2c67918a4f2470d634af77e56 Mon Sep 17 00:00:00 2001 From: Kyle Lacy Date: Sat, 15 Apr 2023 12:51:23 -0700 Subject: [PATCH 04/43] Add `EMAIL_FROM` setting to change sender email for messages --- apiserver/plane/bgtasks/email_verification_task.py | 3 ++- apiserver/plane/bgtasks/forgot_password_task.py | 3 ++- apiserver/plane/bgtasks/magic_link_code_task.py | 3 ++- apiserver/plane/bgtasks/project_invitation_task.py | 3 ++- apiserver/plane/bgtasks/workspace_invitation_task.py | 2 +- apiserver/plane/db/models/user.py | 2 +- apiserver/plane/settings/common.py | 1 + 7 files changed, 11 insertions(+), 6 deletions(-) diff --git a/apiserver/plane/bgtasks/email_verification_task.py b/apiserver/plane/bgtasks/email_verification_task.py index ee4680e53..1da3a7510 100644 --- a/apiserver/plane/bgtasks/email_verification_task.py +++ b/apiserver/plane/bgtasks/email_verification_task.py @@ -2,6 +2,7 @@ from django.core.mail import EmailMultiAlternatives from django.template.loader import render_to_string from django.utils.html import strip_tags +from django.conf import settings # Third party imports from celery import shared_task @@ -20,7 +21,7 @@ def email_verification(first_name, email, token, current_site): realtivelink = "/request-email-verification/" + "?token=" + str(token) abs_url = "http://" + current_site + realtivelink - from_email_string = f"Team Plane " + from_email_string = settings.EMAIL_FROM subject = f"Verify your Email!" diff --git a/apiserver/plane/bgtasks/forgot_password_task.py b/apiserver/plane/bgtasks/forgot_password_task.py index 4598e5f2f..f13f1b89a 100644 --- a/apiserver/plane/bgtasks/forgot_password_task.py +++ b/apiserver/plane/bgtasks/forgot_password_task.py @@ -2,6 +2,7 @@ from django.core.mail import EmailMultiAlternatives from django.template.loader import render_to_string from django.utils.html import strip_tags +from django.conf import settings # Third party imports from celery import shared_task @@ -18,7 +19,7 @@ def forgot_password(first_name, email, uidb64, token, current_site): realtivelink = f"/email-verify/?uidb64={uidb64}&token={token}/" abs_url = "http://" + current_site + realtivelink - from_email_string = f"Team Plane " + from_email_string = settings.EMAIL_FROM subject = f"Verify your Email!" diff --git a/apiserver/plane/bgtasks/magic_link_code_task.py b/apiserver/plane/bgtasks/magic_link_code_task.py index 89554dcca..ea97b0fb8 100644 --- a/apiserver/plane/bgtasks/magic_link_code_task.py +++ b/apiserver/plane/bgtasks/magic_link_code_task.py @@ -2,6 +2,7 @@ from django.core.mail import EmailMultiAlternatives from django.template.loader import render_to_string from django.utils.html import strip_tags +from django.conf import settings # Third party imports from celery import shared_task @@ -14,7 +15,7 @@ def magic_link(email, key, token, current_site): realtivelink = f"/magic-sign-in/?password={token}&key={key}" abs_url = "http://" + current_site + realtivelink - from_email_string = f"Team Plane " + from_email_string = settings.EMAIL_FROM subject = f"Login for Plane" diff --git a/apiserver/plane/bgtasks/project_invitation_task.py b/apiserver/plane/bgtasks/project_invitation_task.py index 18e539970..1c3597120 100644 --- a/apiserver/plane/bgtasks/project_invitation_task.py +++ b/apiserver/plane/bgtasks/project_invitation_task.py @@ -2,6 +2,7 @@ from django.core.mail import EmailMultiAlternatives from django.template.loader import render_to_string from django.utils.html import strip_tags +from django.conf import settings # Third party imports from celery import shared_task @@ -22,7 +23,7 @@ def project_invitation(email, project_id, token, current_site): relativelink = f"/project-member-invitation/{project_member_invite.id}" abs_url = "http://" + current_site + relativelink - from_email_string = f"Team Plane " + from_email_string = settings.EMAIL_FROM subject = f"{project.created_by.first_name or project.created_by.email} invited you to join {project.name} on Plane" diff --git a/apiserver/plane/bgtasks/workspace_invitation_task.py b/apiserver/plane/bgtasks/workspace_invitation_task.py index c6e69689b..0ce32eee0 100644 --- a/apiserver/plane/bgtasks/workspace_invitation_task.py +++ b/apiserver/plane/bgtasks/workspace_invitation_task.py @@ -27,7 +27,7 @@ def workspace_invitation(email, workspace_id, token, current_site, invitor): ) abs_url = "http://" + current_site + realtivelink - from_email_string = f"Team Plane " + from_email_string = settings.EMAIL_FROM subject = f"{invitor or email} invited you to join {workspace.name} on Plane" diff --git a/apiserver/plane/db/models/user.py b/apiserver/plane/db/models/user.py index 334ec3e13..5a4f487c1 100644 --- a/apiserver/plane/db/models/user.py +++ b/apiserver/plane/db/models/user.py @@ -109,7 +109,7 @@ def send_welcome_email(sender, instance, created, **kwargs): if created and not instance.is_bot: first_name = instance.first_name.capitalize() to_email = instance.email - from_email_string = f"Team Plane " + from_email_string = settings.EMAIL_FROM subject = f"Welcome to Plane ✈️!" diff --git a/apiserver/plane/settings/common.py b/apiserver/plane/settings/common.py index b3a6883bd..f5bff248b 100644 --- a/apiserver/plane/settings/common.py +++ b/apiserver/plane/settings/common.py @@ -179,6 +179,7 @@ EMAIL_PORT = int(os.environ.get("EMAIL_PORT", 587)) EMAIL_HOST_USER = os.environ.get("EMAIL_HOST_USER") EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASSWORD") EMAIL_USE_TLS = os.environ.get("EMAIL_USE_TLS", "1") == "1" +EMAIL_FROM = os.environ.get("EMAIL_FROM", "Team Plane ") SIMPLE_JWT = { From 5e5d1a4699599ac9a10b4b4cff89984a93466f97 Mon Sep 17 00:00:00 2001 From: Kyle Lacy Date: Sat, 15 Apr 2023 12:56:43 -0700 Subject: [PATCH 05/43] Update `.env.example` with newly-added env vars --- apiserver/.env.example | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apiserver/.env.example b/apiserver/.env.example index 2e1ee6fd1..a3972379d 100644 --- a/apiserver/.env.example +++ b/apiserver/.env.example @@ -3,10 +3,14 @@ DJANGO_SETTINGS_MODULE="plane.settings.production" DATABASE_URL=postgres://plane:xyzzyspoon@db:5432/plane # Cache REDIS_URL=redis://redis:6379/ +REDIS_BROKER_SSL=0 # SMTP EMAIL_HOST="" EMAIL_HOST_USER="" EMAIL_HOST_PASSWORD="" +EMAIL_PORT="587" +EMAIL_USE_TLS="1" +EMAIL_FROM="Team Plane " # AWS AWS_REGION="" AWS_ACCESS_KEY_ID="" From 792162ae667c4993a3c85449122582be91086e37 Mon Sep 17 00:00:00 2001 From: Kyle Lacy Date: Mon, 17 Apr 2023 12:37:57 -0700 Subject: [PATCH 06/43] Remove redundant `$REDIS_BROKER_SSL` setting A fix was already pushed upstream that made this setting unnecessary --- apiserver/.env.example | 1 - apiserver/plane/settings/production.py | 1 - 2 files changed, 2 deletions(-) diff --git a/apiserver/.env.example b/apiserver/.env.example index a3972379d..e2625c1ed 100644 --- a/apiserver/.env.example +++ b/apiserver/.env.example @@ -3,7 +3,6 @@ DJANGO_SETTINGS_MODULE="plane.settings.production" DATABASE_URL=postgres://plane:xyzzyspoon@db:5432/plane # Cache REDIS_URL=redis://redis:6379/ -REDIS_BROKER_SSL=0 # SMTP EMAIL_HOST="" EMAIL_HOST_USER="" diff --git a/apiserver/plane/settings/production.py b/apiserver/plane/settings/production.py index 68758135e..d8f2a8bb7 100644 --- a/apiserver/plane/settings/production.py +++ b/apiserver/plane/settings/production.py @@ -239,7 +239,6 @@ SLACK_BOT_TOKEN = os.environ.get("SLACK_BOT_TOKEN", False) LOGGER_BASE_URL = os.environ.get("LOGGER_BASE_URL", False) -broker_ssl = os.environ.get("REDIS_BROKER_SSL", "1") == "1" redis_url = os.environ.get("REDIS_URL") broker_url = f"{redis_url}?ssl_cert_reqs={ssl.CERT_NONE.name}&ssl_ca_certs={certifi.where()}" From ca2366aa9b124bb9f0d6a5152f1b5c7d983d9c36 Mon Sep 17 00:00:00 2001 From: Kyle Lacy Date: Mon, 17 Apr 2023 14:07:00 -0700 Subject: [PATCH 07/43] Use env var to set `AWS_S3_ENDPOINT_URL` setting --- apiserver/plane/settings/production.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apiserver/plane/settings/production.py b/apiserver/plane/settings/production.py index d8f2a8bb7..3a590f3b2 100644 --- a/apiserver/plane/settings/production.py +++ b/apiserver/plane/settings/production.py @@ -105,7 +105,7 @@ if ( AWS_S3_ADDRESSING_STYLE = "auto" # The full URL to the S3 endpoint. Leave blank to use the default region URL. - AWS_S3_ENDPOINT_URL = "" + AWS_S3_ENDPOINT_URL = os.environ.get("AWS_S3_ENDPOINT_URL", "") # A prefix to be applied to every stored file. This will be joined to every filename using the "/" separator. AWS_S3_KEY_PREFIX = "" From b8c06b31215c46aa341823bae21db1bbb40d2464 Mon Sep 17 00:00:00 2001 From: Kyle Lacy Date: Mon, 17 Apr 2023 14:08:47 -0700 Subject: [PATCH 08/43] Add `$AWS_S3_ENDPOINT_URL` to `.env.example` --- apiserver/.env.example | 1 + 1 file changed, 1 insertion(+) diff --git a/apiserver/.env.example b/apiserver/.env.example index e2625c1ed..8a7c76ffa 100644 --- a/apiserver/.env.example +++ b/apiserver/.env.example @@ -15,6 +15,7 @@ AWS_REGION="" AWS_ACCESS_KEY_ID="" AWS_SECRET_ACCESS_KEY="" AWS_S3_BUCKET_NAME="" +AWS_S3_ENDPOINT_URL="" # FE WEB_URL="localhost/" # OAUTH From ea87823478b1e2a110357198e356dfc77822f33c Mon Sep 17 00:00:00 2001 From: Kyle Lacy Date: Mon, 17 Apr 2023 16:30:36 -0700 Subject: [PATCH 09/43] Add `$NEXT_PUBLIC_EXTRA_IMAGE_DOMAINS` to add domains for Next Image plugin --- apps/app/.env.example | 3 ++- apps/app/next.config.js | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/app/.env.example b/apps/app/.env.example index 1e2576dfc..cdf30fc72 100644 --- a/apps/app/.env.example +++ b/apps/app/.env.example @@ -1,5 +1,6 @@ # Replace with your instance Public IP # NEXT_PUBLIC_API_BASE_URL = "http://localhost" +NEXT_PUBLIC_EXTRA_IMAGE_DOMAINS= NEXT_PUBLIC_GOOGLE_CLIENTID="" NEXT_PUBLIC_GITHUB_APP_NAME="" NEXT_PUBLIC_GITHUB_ID="" @@ -7,4 +8,4 @@ NEXT_PUBLIC_SENTRY_DSN="" NEXT_PUBLIC_ENABLE_OAUTH=0 NEXT_PUBLIC_ENABLE_SENTRY=0 NEXT_PUBLIC_ENABLE_SESSION_RECORDER=0 -NEXT_PUBLIC_TRACK_EVENTS=0 \ No newline at end of file +NEXT_PUBLIC_TRACK_EVENTS=0 diff --git a/apps/app/next.config.js b/apps/app/next.config.js index d194a6cc7..b3c67eedd 100644 --- a/apps/app/next.config.js +++ b/apps/app/next.config.js @@ -1,5 +1,6 @@ const { withSentryConfig } = require("@sentry/nextjs"); const path = require("path"); +const extraImageDomains = (process.env.NEXT_PUBLIC_EXTRA_IMAGE_DOMAINS ?? "").split(",").filter((domain) => domain.length > 0); const nextConfig = { reactStrictMode: false, @@ -11,6 +12,7 @@ const nextConfig = { "planefs.s3.amazonaws.com", "images.unsplash.com", "avatars.githubusercontent.com", + ...extraImageDomains, ], }, output: "standalone", From 63a36fb25d915fd71eeaba9f3e25a44d06511961 Mon Sep 17 00:00:00 2001 From: Dakshesh Jain <65905942+dakshesh14@users.noreply.github.com> Date: Wed, 19 Apr 2023 15:40:31 +0530 Subject: [PATCH 10/43] feat: jira importer (#879) * feat: jira importer * fix: yarn lock * fix: displaying correct count of users that are been imported * fix: showing workspace member in import users --- apps/app/components/integration/guide.tsx | 2 +- .../integration/jira/confirm-import.tsx | 49 ++++ .../integration/jira/give-details.tsx | 178 ++++++++++++++ .../integration/jira/import-users.tsx | 145 +++++++++++ apps/app/components/integration/jira/index.ts | 38 +++ .../integration/jira/jira-project-detail.tsx | 168 +++++++++++++ apps/app/components/integration/jira/root.tsx | 225 +++++++++++++++++- .../components/ui/custom-search-select.tsx | 6 +- apps/app/components/ui/index.ts | 1 + apps/app/components/ui/toggle-switch.tsx | 32 +++ apps/app/constants/fetch-keys.ts | 11 +- apps/app/constants/workspace.ts | 14 +- apps/app/hooks/use-workspace-members.tsx | 43 ++++ apps/app/package.json | 2 + apps/app/services/integration/jira.service.ts | 34 +++ apps/app/types/importer/index.ts | 1 + apps/app/types/importer/jira-importer.d.ts | 58 +++++ yarn.lock | 10 + 18 files changed, 1006 insertions(+), 11 deletions(-) create mode 100644 apps/app/components/integration/jira/confirm-import.tsx create mode 100644 apps/app/components/integration/jira/give-details.tsx create mode 100644 apps/app/components/integration/jira/import-users.tsx create mode 100644 apps/app/components/integration/jira/jira-project-detail.tsx create mode 100644 apps/app/components/ui/toggle-switch.tsx create mode 100644 apps/app/hooks/use-workspace-members.tsx create mode 100644 apps/app/services/integration/jira.service.ts create mode 100644 apps/app/types/importer/jira-importer.d.ts diff --git a/apps/app/components/integration/guide.tsx b/apps/app/components/integration/guide.tsx index ce33f16d2..ad9ffe559 100644 --- a/apps/app/components/integration/guide.tsx +++ b/apps/app/components/integration/guide.tsx @@ -52,7 +52,7 @@ const IntegrationGuide = () => { handleClose={() => setDeleteImportModal(false)} data={importToDelete} /> -
+
{!provider && ( <>
diff --git a/apps/app/components/integration/jira/confirm-import.tsx b/apps/app/components/integration/jira/confirm-import.tsx new file mode 100644 index 000000000..79c56bf4b --- /dev/null +++ b/apps/app/components/integration/jira/confirm-import.tsx @@ -0,0 +1,49 @@ +import React from "react"; + +// react hook form +import { useFormContext } from "react-hook-form"; + +// types +import { IJiraImporterForm } from "types"; + +export const JiraConfirmImport: React.FC = () => { + const { watch } = useFormContext(); + + return ( +
+
+
+

Confirm

+
+ +
+

Migrating

+
+
+
+

{watch("data.total_issues")}

+

Issues

+
+
+

{watch("data.total_states")}

+

States

+
+
+

{watch("data.total_modules")}

+

Modules

+
+
+

{watch("data.total_labels")}

+

Labels

+
+
+

+ {watch("data.users").filter((user) => user.import).length} +

+

User

+
+
+
+
+ ); +}; diff --git a/apps/app/components/integration/jira/give-details.tsx b/apps/app/components/integration/jira/give-details.tsx new file mode 100644 index 000000000..fb4032b68 --- /dev/null +++ b/apps/app/components/integration/jira/give-details.tsx @@ -0,0 +1,178 @@ +import React from "react"; + +// next +import Link from "next/link"; + +// react hook form +import { useFormContext, Controller } from "react-hook-form"; + +// icons +import { PlusIcon } from "@heroicons/react/20/solid"; + +// hooks +import useProjects from "hooks/use-projects"; + +// components +import { Input, CustomSelect } from "components/ui"; + +import { IJiraImporterForm } from "types"; + +export const JiraGetImportDetail: React.FC = () => { + const { + register, + control, + formState: { errors }, + } = useFormContext(); + + const { projects } = useProjects(); + + return ( +
+
+
+

Jira Personal Access Token

+

+ Get to know your access token by navigating to{" "} + + + Atlassian Settings + + +

+
+ +
+ +
+
+ +
+
+

Jira Project Key

+

If XXX-123 is your issue, then enter XXX

+
+
+ +
+
+ +
+
+

Jira Email Address

+

+ Enter the Gmail account that you use in Jira account +

+
+
+ +
+
+ +
+
+

Jira Installation or Cloud Host Name

+

Enter your companies cloud host name

+
+
+ +
+
+ +
+
+

Import to project

+

Select which project you want to import to.

+
+
+ ( + + {value && value !== "" + ? projects.find((p) => p.id === value)?.name + : "Select Project"} + + } + > + {projects.length > 0 ? ( + projects.map((project) => ( + + {project.name} + + )) + ) : ( +
+

You don{"'"}t have any project. Please create a project first.

+
+ )} +
+ +
+
+ )} + /> +
+
+
+ ); +}; diff --git a/apps/app/components/integration/jira/import-users.tsx b/apps/app/components/integration/jira/import-users.tsx new file mode 100644 index 000000000..fa48bc772 --- /dev/null +++ b/apps/app/components/integration/jira/import-users.tsx @@ -0,0 +1,145 @@ +import { FC } from "react"; + +// next +import { useRouter } from "next/router"; + +// react-hook-form +import { useFormContext, useFieldArray, Controller } from "react-hook-form"; + +// hooks +import useWorkspaceMembers from "hooks/use-workspace-members"; + +// components +import { ToggleSwitch, Input, CustomSelect, CustomSearchSelect, Avatar } from "components/ui"; + +import { IJiraImporterForm } from "types"; + +export const JiraImportUsers: FC = () => { + const { + control, + watch, + register, + formState: { errors }, + } = useFormContext(); + + const { fields } = useFieldArray({ + control, + name: "data.users", + }); + + const router = useRouter(); + const { workspaceSlug } = router.query; + + const { workspaceMembers: members } = useWorkspaceMembers(workspaceSlug?.toString()); + + const options = + members?.map((member) => ({ + value: member.member.email, + query: + (member.member.first_name && member.member.first_name !== "" + ? member.member.first_name + : member.member.email) + + " " + + member.member.last_name ?? "", + content: ( +
+ + {member.member.first_name && member.member.first_name !== "" + ? member.member.first_name + " (" + member.member.email + ")" + : member.member.email} +
+ ), + })) ?? []; + + return ( +
+
+
+

Users

+

Update, invite or choose not to invite assignee

+
+
+ ( + + )} + /> +
+
+ + {watch("data.invite_users") && ( +
+
+
Name
+
Import as
+
+ +
+ {fields.map((user, index) => ( +
+
+

{user.username}

+
+
+ ( + + {Boolean(value) ? value : ("Ignore" as any)} + + } + > + Invite by email + Map to existing + Do not import + + )} + /> +
+
+ {watch(`data.users.${index}.import`) === "invite" && ( + + )} + {watch(`data.users.${index}.import`) === "map" && ( + ( + + )} + /> + )} +
+
+ ))} +
+
+ )} +
+ ); +}; diff --git a/apps/app/components/integration/jira/index.ts b/apps/app/components/integration/jira/index.ts index 1efe34c51..321e4f313 100644 --- a/apps/app/components/integration/jira/index.ts +++ b/apps/app/components/integration/jira/index.ts @@ -1 +1,39 @@ export * from "./root"; +export * from "./give-details"; +export * from "./jira-project-detail"; +export * from "./import-users"; +export * from "./confirm-import"; + +import { IJiraImporterForm } from "types"; + +export type TJiraIntegrationSteps = + | "import-configure" + | "display-import-data" + | "select-import-data" + | "import-users" + | "import-confirmation"; + +export interface IJiraIntegrationData { + state: TJiraIntegrationSteps; +} + +export const jiraFormDefaultValues: IJiraImporterForm = { + metadata: { + cloud_hostname: "", + api_token: "", + project_key: "", + email: "", + }, + config: { + epics_to_modules: false, + }, + data: { + users: [], + invite_users: true, + total_issues: 0, + total_labels: 0, + total_modules: 0, + total_states: 0, + }, + project_id: "", +}; diff --git a/apps/app/components/integration/jira/jira-project-detail.tsx b/apps/app/components/integration/jira/jira-project-detail.tsx new file mode 100644 index 000000000..48220a8c1 --- /dev/null +++ b/apps/app/components/integration/jira/jira-project-detail.tsx @@ -0,0 +1,168 @@ +import React, { useEffect } from "react"; + +// next +import { useRouter } from "next/router"; + +// swr +import useSWR from "swr"; + +// react hook form +import { useFormContext, Controller } from "react-hook-form"; + +// services +import jiraImporterService from "services/integration/jira.service"; + +// fetch keys +import { JIRA_IMPORTER_DETAIL } from "constants/fetch-keys"; + +import { IJiraImporterForm, IJiraMetadata } from "types"; + +// components +import { Spinner, ToggleSwitch } from "components/ui"; + +import type { IJiraIntegrationData, TJiraIntegrationSteps } from "./"; + +type Props = { + setCurrentStep: React.Dispatch>; + setDisableTopBarAfter: React.Dispatch>; +}; + +export const JiraProjectDetail: React.FC = (props) => { + const { setCurrentStep, setDisableTopBarAfter } = props; + + const { + watch, + setValue, + control, + formState: { errors }, + } = useFormContext(); + + const router = useRouter(); + const { workspaceSlug } = router.query; + + const params: IJiraMetadata = { + api_token: watch("metadata.api_token"), + project_key: watch("metadata.project_key"), + email: watch("metadata.email"), + cloud_hostname: watch("metadata.cloud_hostname"), + }; + + const { data: projectInfo, error } = useSWR( + workspaceSlug && + !errors.metadata?.api_token && + !errors.metadata?.project_key && + !errors.metadata?.email && + !errors.metadata?.cloud_hostname + ? JIRA_IMPORTER_DETAIL(workspaceSlug.toString(), params) + : null, + workspaceSlug && + !errors.metadata?.api_token && + !errors.metadata?.project_key && + !errors.metadata?.email && + !errors.metadata?.cloud_hostname + ? () => jiraImporterService.getJiraProjectInfo(workspaceSlug.toString(), params) + : null + ); + + useEffect(() => { + if (!projectInfo) return; + + setValue("data.total_issues", projectInfo.issues); + setValue("data.total_labels", projectInfo.labels); + setValue( + "data.users", + projectInfo.users?.map((user) => ({ + email: user.emailAddress, + import: false, + username: user.displayName, + })) + ); + setValue("data.total_states", projectInfo.states); + setValue("data.total_modules", projectInfo.modules); + }, [projectInfo, setValue]); + + useEffect(() => { + if (error) setDisableTopBarAfter("display-import-data"); + else setDisableTopBarAfter(null); + }, [error, setDisableTopBarAfter]); + + useEffect(() => { + if (!projectInfo && !error) setDisableTopBarAfter("display-import-data"); + else if (!error) setDisableTopBarAfter(null); + }, [projectInfo, error, setDisableTopBarAfter]); + + if (!projectInfo && !error) { + return ( +
+ +
+ ); + } + + if (error) { + return ( +
+

+ Something went wrong. Please{" "} + {" "} + and check your Jira project details. +

+
+ ); + } + + return ( +
+
+
+

Import Data

+

Import Completed. We have found:

+
+
+
+

{projectInfo?.issues}

+

Issues

+
+
+

{projectInfo?.states}

+

States

+
+
+

{projectInfo?.modules}

+

Modules

+
+
+

{projectInfo?.labels}

+

Labels

+
+
+

{projectInfo?.users?.length}

+

Users

+
+
+
+ +
+
+

Import Epics

+

Import epics as modules

+
+
+ ( + + )} + /> +
+
+
+ ); +}; diff --git a/apps/app/components/integration/jira/root.tsx b/apps/app/components/integration/jira/root.tsx index 15a89c3e4..b9bd732c0 100644 --- a/apps/app/components/integration/jira/root.tsx +++ b/apps/app/components/integration/jira/root.tsx @@ -1 +1,224 @@ -export const JiraImporterRoot = () => <>; +import React, { useState } from "react"; + +// next +import Link from "next/link"; +import Image from "next/image"; +import { useRouter } from "next/router"; + +// swr +import { mutate } from "swr"; + +// react hook form +import { FormProvider, useForm } from "react-hook-form"; + +// icons +import { ArrowLeftIcon, ListBulletIcon } from "@heroicons/react/24/outline"; +import { CogIcon, CloudUploadIcon, UsersIcon, CheckIcon } from "components/icons"; + +// services +import jiraImporterService from "services/integration/jira.service"; + +// fetch keys +import { IMPORTER_SERVICES_LIST } from "constants/fetch-keys"; + +// components +import { PrimaryButton, SecondaryButton } from "components/ui"; +import { + JiraGetImportDetail, + JiraProjectDetail, + JiraImportUsers, + JiraConfirmImport, + jiraFormDefaultValues, + TJiraIntegrationSteps, + IJiraIntegrationData, +} from "./"; + +import JiraLogo from "public/services/jira.png"; + +import { IJiraImporterForm } from "types"; + +const integrationWorkflowData: Array<{ + title: string; + key: TJiraIntegrationSteps; + icon: React.FC>; +}> = [ + { + title: "Configure", + key: "import-configure", + icon: CogIcon, + }, + { + title: "Import Data", + key: "display-import-data", + icon: ListBulletIcon, + }, + { + title: "Users", + key: "import-users", + icon: UsersIcon, + }, + { + title: "Confirm", + key: "import-confirmation", + icon: CheckIcon, + }, +]; + +export const JiraImporterRoot = () => { + const [currentStep, setCurrentStep] = useState({ + state: "import-configure", + }); + const [disableTopBarAfter, setDisableTopBarAfter] = useState(null); + + const router = useRouter(); + const { workspaceSlug } = router.query; + + const methods = useForm({ + defaultValues: jiraFormDefaultValues, + mode: "all", + reValidateMode: "onChange", + }); + + const isValid = methods.formState.isValid; + + const onSubmit = async (data: IJiraImporterForm) => { + if (!workspaceSlug) return; + + await jiraImporterService + .createJiraImporter(workspaceSlug.toString(), data) + .then(() => { + mutate(IMPORTER_SERVICES_LIST(workspaceSlug.toString())); + router.push(`/${workspaceSlug}/settings/import-export`); + }) + .catch((err) => { + console.log(err); + }); + }; + + const activeIntegrationState = () => { + const currentElementIndex = integrationWorkflowData.findIndex( + (i) => i?.key === currentStep?.state + ); + + return currentElementIndex; + }; + + return ( +
+ +
+
+ +
+
Cancel import & go back
+
+ + +
+
+
+ jira logo +
+
+ {integrationWorkflowData.map((integration, index) => ( + + + {index < integrationWorkflowData.length - 1 && ( +
+ {" "} +
+ )} +
+ ))} +
+
+ +
+ +
+
+ {currentStep.state === "import-configure" && } + {currentStep.state === "display-import-data" && ( + + )} + {currentStep?.state === "import-users" && } + {currentStep?.state === "import-confirmation" && } +
+ +
+ {currentStep?.state !== "import-configure" && ( + { + const currentElementIndex = integrationWorkflowData.findIndex( + (i) => i?.key === currentStep?.state + ); + setCurrentStep({ + state: integrationWorkflowData[currentElementIndex - 1]?.key, + }); + }} + > + Back + + )} + { + const currentElementIndex = integrationWorkflowData.findIndex( + (i) => i?.key === currentStep?.state + ); + + if (currentElementIndex === integrationWorkflowData.length - 1) { + methods.handleSubmit(onSubmit)(); + } else { + setCurrentStep({ + state: integrationWorkflowData[currentElementIndex + 1]?.key, + }); + } + }} + > + {currentStep?.state === "import-confirmation" ? "Confirm & Import" : "Next"} + +
+
+
+
+
+
+ ); +}; diff --git a/apps/app/components/ui/custom-search-select.tsx b/apps/app/components/ui/custom-search-select.tsx index c8cea10be..fd7188db6 100644 --- a/apps/app/components/ui/custom-search-select.tsx +++ b/apps/app/components/ui/custom-search-select.tsx @@ -18,6 +18,7 @@ type CustomSearchSelectProps = { noChevron?: boolean; customButton?: JSX.Element; optionsClassName?: string; + input?: boolean; disabled?: boolean; selfPositioned?: boolean; multiple?: boolean; @@ -34,6 +35,7 @@ export const CustomSearchSelect = ({ noChevron = false, customButton, optionsClassName = "", + input = false, disabled = false, selfPositioned = false, multiple = false, @@ -68,7 +70,9 @@ export const CustomSearchSelect = ({ void; + label?: string; + disabled?: boolean; + className?: string; +}; + +export const ToggleSwitch: React.FC = (props) => { + const { value, onChange, label, disabled, className } = props; + + return ( + + {label} + + ); +}; diff --git a/apps/app/constants/fetch-keys.ts b/apps/app/constants/fetch-keys.ts index aff1d99f4..7090d1f18 100644 --- a/apps/app/constants/fetch-keys.ts +++ b/apps/app/constants/fetch-keys.ts @@ -1,3 +1,5 @@ +import { IJiraMetadata } from "types"; + const paramsToKey = (params: any) => { const { state, priority, assignees, created_by, labels } = params; @@ -122,6 +124,12 @@ export const APP_INTEGRATIONS = "APP_INTEGRATIONS"; export const WORKSPACE_INTEGRATIONS = (workspaceSlug: string) => `WORKSPACE_INTEGRATIONS_${workspaceSlug.toUpperCase()}`; +export const JIRA_IMPORTER_DETAIL = (workspaceSlug: string, params: IJiraMetadata) => { + const { api_token, cloud_hostname, email, project_key } = params; + + return `JIRA_IMPORTER_DETAIL_${workspaceSlug.toUpperCase()}_${api_token}_${cloud_hostname}_${email}_${project_key}`; +}; + //import-export export const IMPORTER_SERVICES_LIST = (workspaceSlug: string) => `IMPORTER_SERVICES_LIST_${workspaceSlug.toUpperCase()}`; @@ -153,6 +161,7 @@ export const PAGE_BLOCK_DETAILS = (pageId: string) => `PAGE_BLOCK_DETAILS_${page // estimates export const ESTIMATES_LIST = (projectId: string) => `ESTIMATES_LIST_${projectId.toUpperCase()}`; -export const ESTIMATE_DETAILS = (estimateId: string) => `ESTIMATE_DETAILS_${estimateId.toUpperCase()}`; +export const ESTIMATE_DETAILS = (estimateId: string) => + `ESTIMATE_DETAILS_${estimateId.toUpperCase()}`; export const ESTIMATE_POINTS_LIST = (estimateId: string) => `ESTIMATES_POINTS_LIST_${estimateId.toUpperCase()}`; diff --git a/apps/app/constants/workspace.ts b/apps/app/constants/workspace.ts index 2864855d3..24f886af9 100644 --- a/apps/app/constants/workspace.ts +++ b/apps/app/constants/workspace.ts @@ -75,11 +75,11 @@ export const IMPORTERS_EXPORTERS_LIST = [ description: "Import issues from GitHub repositories and sync them.", logo: GithubLogo, }, - // { - // provider: "jira", - // type: "import", - // title: "Jira", - // description: "Import issues and epics from Jira projects and epics.", - // logo: JiraLogo, - // }, + { + provider: "jira", + type: "import", + title: "Jira", + description: "Import issues and epics from Jira projects and epics.", + logo: JiraLogo, + }, ]; diff --git a/apps/app/hooks/use-workspace-members.tsx b/apps/app/hooks/use-workspace-members.tsx new file mode 100644 index 000000000..b41f4535d --- /dev/null +++ b/apps/app/hooks/use-workspace-members.tsx @@ -0,0 +1,43 @@ +import useSWR from "swr"; +// services +import workspaceService from "services/workspace.service"; +// fetch-keys +import { WORKSPACE_MEMBERS } from "constants/fetch-keys"; +// hooks +import useUser from "./use-user"; + +const useWorkspaceMembers = (workspaceSlug?: string) => { + const { user } = useUser(); + + const { data: workspaceMembers, error: workspaceMemberErrors } = useSWR( + workspaceSlug ? WORKSPACE_MEMBERS(workspaceSlug) : null, + workspaceSlug ? () => workspaceService.workspaceMembers(workspaceSlug) : null + ); + + const hasJoined = workspaceMembers?.some((item: any) => item.member.id === (user as any)?.id); + + const isOwner = workspaceMembers?.some( + (item) => item.member.id === (user as any)?.id && item.role === 20 + ); + const isMember = workspaceMembers?.some( + (item) => item.member.id === (user as any)?.id && item.role === 15 + ); + const isViewer = workspaceMembers?.some( + (item) => item.member.id === (user as any)?.id && item.role === 10 + ); + const isGuest = workspaceMembers?.some( + (item) => item.member.id === (user as any)?.id && item.role === 5 + ); + + return { + workspaceMembers, + workspaceMemberErrors, + hasJoined, + isOwner, + isMember, + isViewer, + isGuest, + }; +}; + +export default useWorkspaceMembers; diff --git a/apps/app/package.json b/apps/app/package.json index 8279ecb2d..5374c6363 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -56,6 +56,8 @@ "eslint-config-custom": "*", "eslint-config-next": "12.2.2", "postcss": "^8.4.14", + "prettier": "^2.8.7", + "prettier-plugin-tailwindcss": "^0.2.7", "tailwindcss": "^3.1.6", "tsconfig": "*", "typescript": "4.7.4" diff --git a/apps/app/services/integration/jira.service.ts b/apps/app/services/integration/jira.service.ts new file mode 100644 index 000000000..20ad8166a --- /dev/null +++ b/apps/app/services/integration/jira.service.ts @@ -0,0 +1,34 @@ +import APIService from "services/api.service"; + +// types +import { IJiraMetadata, IJiraResponse, IJiraImporterForm } from "types"; + +const { NEXT_PUBLIC_API_BASE_URL } = process.env; + +class JiraImportedService extends APIService { + constructor() { + super(NEXT_PUBLIC_API_BASE_URL || "http://localhost:8000"); + } + + async getJiraProjectInfo(workspaceSlug: string, params: IJiraMetadata): Promise { + return this.get(`/api/workspaces/${workspaceSlug}/importers/jira`, { + params, + }) + .then((response) => response?.data) + .catch((error) => { + throw error?.response?.data; + }); + } + + async createJiraImporter(workspaceSlug: string, data: IJiraImporterForm): Promise { + return this.post(`/api/workspaces/${workspaceSlug}/projects/importers/jira/`, data) + .then((response) => response?.data) + .catch((error) => { + throw error?.response?.data; + }); + } +} + +const jiraImporterService = new JiraImportedService(); + +export default jiraImporterService; diff --git a/apps/app/types/importer/index.ts b/apps/app/types/importer/index.ts index 1e3f8c2ad..134248a71 100644 --- a/apps/app/types/importer/index.ts +++ b/apps/app/types/importer/index.ts @@ -1,4 +1,5 @@ export * from "./github-importer"; +export * from "./jira-importer"; import { IProjectLite } from "types/projects"; // types diff --git a/apps/app/types/importer/jira-importer.d.ts b/apps/app/types/importer/jira-importer.d.ts new file mode 100644 index 000000000..cd9e31ad2 --- /dev/null +++ b/apps/app/types/importer/jira-importer.d.ts @@ -0,0 +1,58 @@ +export interface IJiraImporterForm { + metadata: IJiraMetadata; + config: IJiraConfig; + data: IJiraData; + project_id: string; +} + +export interface IJiraConfig { + epics_to_modules: boolean; +} + +export interface IJiraData { + users: User[]; + invite_users: boolean; + total_issues: number; + total_labels: number; + total_states: number; + total_modules: number; +} + +export interface User { + username: string; + import: "invite" | "map" | false; + email: string; +} + +export interface IJiraMetadata { + cloud_hostname: string; + api_token: string; + project_key: string; + email: string; +} + +export interface IJiraResponse { + issues: number; + modules: number; + labels: number; + states: number; + users: IJiraResponseUser[]; +} + +export interface IJiraResponseUser { + self: string; + accountId: string; + accountType: string; + emailAddress: string; + avatarUrls: AvatarUrls; + displayName: string; + active: boolean; + locale: string; +} + +export interface IJiraResponseAvatarUrls { + "48x48": string; + "24x24": string; + "16x16": string; + "32x32": string; +} diff --git a/yarn.lock b/yarn.lock index ae33bba9a..99ac4cf56 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6485,6 +6485,16 @@ prelude-ls@^1.2.1: resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +prettier-plugin-tailwindcss@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.2.7.tgz#2314d728cce9c9699ced41a01253eb48b4218da5" + integrity sha512-jQopIOgjLpX+y8HeD56XZw7onupRTC0cw7eKKUimI7vhjkPF5/1ltW5LyqaPtSyc8HvEpvNZsvvsGFa2qpa59w== + +prettier@^2.8.7: + version "2.8.7" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" + integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== + prettier@latest: version "2.8.4" resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz" From ac125965eb4bbe74a5c9b6757b0d631b3f9d7f43 Mon Sep 17 00:00:00 2001 From: Dakshesh Jain <65905942+dakshesh14@users.noreply.github.com> Date: Wed, 19 Apr 2023 15:40:47 +0530 Subject: [PATCH 11/43] fix: add tlds (#851) --- apps/app/components/rich-text-editor/index.tsx | 7 ++++++- apps/app/package.json | 1 + yarn.lock | 5 +++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/app/components/rich-text-editor/index.tsx b/apps/app/components/rich-text-editor/index.tsx index 32b4931f1..701a96562 100644 --- a/apps/app/components/rich-text-editor/index.tsx +++ b/apps/app/components/rich-text-editor/index.tsx @@ -29,6 +29,8 @@ import { OnChangeHTML, } from "@remirror/react"; import { TableExtension } from "@remirror/extension-react-tables"; +// tlds +import tlds from "tlds"; // services import fileService from "services/file.service"; // ui @@ -136,7 +138,10 @@ const RemirrorRichTextEditor: FC = (props) => { new CodeExtension(), new PlaceholderExtension({ placeholder: placeholder || "Enter text..." }), new HistoryExtension(), - new LinkExtension({ autoLink: true }), + new LinkExtension({ + autoLink: true, + autoLinkAllowedTLDs: tlds, + }), new ImageExtension({ enableResizing: true, uploadHandler: uploadImageHandler, diff --git a/apps/app/package.json b/apps/app/package.json index 5374c6363..823541cb2 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -38,6 +38,7 @@ "recharts": "^2.3.2", "remirror": "^2.0.23", "swr": "^1.3.0", + "tlds": "^1.238.0", "uuid": "^9.0.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 99ac4cf56..1e8bb41f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7763,6 +7763,11 @@ tinycolor2@^1.4.1: resolved "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz" integrity sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw== +tlds@^1.238.0: + version "1.238.0" + resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.238.0.tgz#ffe7c19c8940c35b497cda187a6927f9450325a4" + integrity sha512-lFPF9pZFhLrPodaJ0wt9QIN0l8jOxqmUezGZnm7BfkDSVd9q667oVIJukLVzhF+4oW7uDlrLlfJrL5yu9RWwew== + to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" From fb1932e3097ad290954a57d5e20966d7eaa067c9 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Wed, 19 Apr 2023 15:41:17 +0530 Subject: [PATCH 12/43] fix : create issue modal (#875) * fix: label list bug fix * fix: assignee and label count removed * fix: assignee and label fix --- apps/app/components/issues/select/assignee.tsx | 3 +-- apps/app/components/issues/select/label.tsx | 3 +-- apps/app/components/ui/labels-list.tsx | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/app/components/issues/select/assignee.tsx b/apps/app/components/issues/select/assignee.tsx index d47d4b178..1d06f3f0e 100644 --- a/apps/app/components/issues/select/assignee.tsx +++ b/apps/app/components/issues/select/assignee.tsx @@ -57,8 +57,7 @@ export const IssueAssigneeSelect: React.FC = ({ projectId, value = [], on
{value && value.length > 0 && Array.isArray(value) ? (
- - {value.length} Assignees +
) : (
diff --git a/apps/app/components/issues/select/label.tsx b/apps/app/components/issues/select/label.tsx index c05c8a8c8..2adeaa222 100644 --- a/apps/app/components/issues/select/label.tsx +++ b/apps/app/components/issues/select/label.tsx @@ -73,9 +73,8 @@ export const IssueLabelSelect: React.FC = ({ setIsOpen, value, onChange, issueLabels?.find((l) => l.id === v)?.color) ?? []} length={3} - showLength + showLength={true} /> - {value.length} Labels ) : ( diff --git a/apps/app/components/ui/labels-list.tsx b/apps/app/components/ui/labels-list.tsx index 2b296d624..ceb8d7e3a 100644 --- a/apps/app/components/ui/labels-list.tsx +++ b/apps/app/components/ui/labels-list.tsx @@ -14,7 +14,7 @@ export const IssueLabelsList: React.FC = ({ <> {labels && ( <> - {labels.map((color, index) => ( + {labels.slice(0, length).map((color, index) => (
Date: Wed, 19 Apr 2023 15:41:46 +0530 Subject: [PATCH 13/43] fix: invite button validation , style: workspace screen (#877) * fix: invite button validation * style: workspace screen tab height --- apps/app/components/onboarding/invite-members.tsx | 3 ++- apps/app/components/onboarding/workspace.tsx | 12 ++++++------ .../components/workspace/create-workspace-form.tsx | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/app/components/onboarding/invite-members.tsx b/apps/app/components/onboarding/invite-members.tsx index a731829b4..8c257ad9d 100644 --- a/apps/app/components/onboarding/invite-members.tsx +++ b/apps/app/components/onboarding/invite-members.tsx @@ -34,6 +34,7 @@ export const InviteMembers: React.FC = ({ setStep, workspace }) => { .catch((err) => console.log(err)); }; + const checkEmail = watch("emails") && watch("emails").length > 0 ; return (
= ({ setStep, workspace }) => { {isSubmitting ? "Inviting..." : "Continue"} diff --git a/apps/app/components/onboarding/workspace.tsx b/apps/app/components/onboarding/workspace.tsx index c4c5de4f7..7432fd7a7 100644 --- a/apps/app/components/onboarding/workspace.tsx +++ b/apps/app/components/onboarding/workspace.tsx @@ -65,10 +65,10 @@ export const Workspace: React.FC = ({ setStep, setWorkspace }) => { }; return ( -
+
= ({ setStep, setWorkspace }) => { Invited Workspace - + { @@ -108,9 +108,9 @@ export const Workspace: React.FC = ({ setStep, setWorkspace }) => { setDefaultValues={setDefaultValues} /> - -
-
+ +
+
{invitations && invitations.length > 0 ? ( invitations.map((invitation) => (
diff --git a/apps/app/components/workspace/create-workspace-form.tsx b/apps/app/components/workspace/create-workspace-form.tsx index b663c6fb8..10d9ed919 100644 --- a/apps/app/components/workspace/create-workspace-form.tsx +++ b/apps/app/components/workspace/create-workspace-form.tsx @@ -100,7 +100,7 @@ export const CreateWorkspaceForm: React.FC = ({ onSubmit={handleSubmit(handleCreateWorkspace)} >
-
+
Workspace name From c5206a7792c4994de46bfe1bee6580fb3326e372 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Wed, 19 Apr 2023 17:25:31 +0530 Subject: [PATCH 14/43] feat: separate filters for cycles and modules (#892) --- apps/app/contexts/issue-view.context.tsx | 152 ++++++++++++++++++++--- apps/app/services/cycles.service.ts | 2 +- apps/app/services/modules.service.ts | 6 +- apps/app/types/cycles.d.ts | 13 +- apps/app/types/modules.d.ts | 4 + 5 files changed, 156 insertions(+), 21 deletions(-) diff --git a/apps/app/contexts/issue-view.context.tsx b/apps/app/contexts/issue-view.context.tsx index 8205d9277..ff17007ab 100644 --- a/apps/app/contexts/issue-view.context.tsx +++ b/apps/app/contexts/issue-view.context.tsx @@ -8,6 +8,8 @@ import useSWR, { mutate } from "swr"; import ToastAlert from "components/toast-alert"; // services import projectService from "services/project.service"; +import cyclesService from "services/cycles.service"; +import modulesService from "services/modules.service"; import viewsService from "services/views.service"; // types import { @@ -18,7 +20,12 @@ import { TIssueOrderByOptions, } from "types"; // fetch-keys -import { USER_PROJECT_VIEW, VIEW_DETAILS } from "constants/fetch-keys"; +import { + CYCLE_DETAILS, + MODULE_DETAILS, + USER_PROJECT_VIEW, + VIEW_DETAILS, +} from "constants/fetch-keys"; export const issueViewContext = createContext({} as ContextType); @@ -171,7 +178,29 @@ const saveDataToServer = async (workspaceSlug: string, projectID: string, state: }); }; -const sendFilterDataToServer = async ( +const saveCycleFilters = async ( + workspaceSlug: string, + projectId: string, + cycleId: string, + state: any +) => { + await cyclesService.patchCycle(workspaceSlug, projectId, cycleId, { + ...state, + }); +}; + +const saveModuleFilters = async ( + workspaceSlug: string, + projectId: string, + moduleId: string, + state: any +) => { + await modulesService.patchModule(workspaceSlug, projectId, moduleId, { + ...state, + }); +}; + +const saveViewFilters = async ( workspaceSlug: string, projectId: string, viewId: string, @@ -206,7 +235,7 @@ export const IssueViewContextProvider: React.FC<{ children: React.ReactNode }> = const [state, dispatch] = useReducer(reducer, initialState); const router = useRouter(); - const { workspaceSlug, projectId, viewId } = router.query; + const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query; const { data: myViewProps, mutate: mutateMyViewProps } = useSWR( workspaceSlug && projectId ? USER_PROJECT_VIEW(projectId as string) : null, @@ -227,6 +256,30 @@ export const IssueViewContextProvider: React.FC<{ children: React.ReactNode }> = : null ); + const { data: cycleDetails, mutate: mutateCycleDetails } = useSWR( + workspaceSlug && projectId && cycleId ? CYCLE_DETAILS(cycleId as string) : null, + workspaceSlug && projectId && cycleId + ? () => + cyclesService.getCycleDetails( + workspaceSlug.toString(), + projectId.toString(), + cycleId.toString() + ) + : null + ); + + const { data: moduleDetails, mutate: mutateModuleDetails } = useSWR( + workspaceSlug && projectId && moduleId ? MODULE_DETAILS(moduleId.toString()) : null, + workspaceSlug && projectId && moduleId + ? () => + modulesService.getModuleDetails( + workspaceSlug.toString(), + projectId.toString(), + moduleId.toString() + ) + : null + ); + const setIssueView = useCallback( (property: TIssueViewOptions) => { dispatch({ @@ -352,9 +405,8 @@ export const IssueViewContextProvider: React.FC<{ children: React.ReactNode }> = const setFilters = useCallback( (property: Partial, saveToServer = true) => { Object.keys(property).forEach((key) => { - if (property[key as keyof typeof property]?.length === 0) { + if (property[key as keyof typeof property]?.length === 0) property[key as keyof typeof property] = null; - } }); dispatch({ @@ -369,22 +421,53 @@ export const IssueViewContextProvider: React.FC<{ children: React.ReactNode }> = if (!workspaceSlug || !projectId) return; - mutateMyViewProps((prevData) => { - if (!prevData) return prevData; + if (cycleId) { + mutateCycleDetails((prevData: any) => { + if (!prevData) return prevData; - return { - ...prevData, + return { + ...prevData, + view_props: { + filters: { + ...state.filters, + ...property, + }, + }, + }; + }, false); + + saveCycleFilters(workspaceSlug.toString(), projectId.toString(), cycleId.toString(), { view_props: { - ...state, filters: { ...state.filters, ...property, }, }, - }; - }, false); + }); + } else if (moduleId) { + mutateModuleDetails((prevData: any) => { + if (!prevData) return prevData; - if (viewId) { + return { + ...prevData, + view_props: { + filters: { + ...state.filters, + ...property, + }, + }, + }; + }, false); + + saveModuleFilters(workspaceSlug.toString(), projectId.toString(), moduleId.toString(), { + view_props: { + filters: { + ...state.filters, + ...property, + }, + }, + }); + } else if (viewId) { mutateViewDetails((prevData: any) => { if (!prevData) return prevData; return { @@ -396,13 +479,28 @@ export const IssueViewContextProvider: React.FC<{ children: React.ReactNode }> = }; }, false); if (saveToServer) - sendFilterDataToServer(workspaceSlug as string, projectId as string, viewId as string, { + saveViewFilters(workspaceSlug as string, projectId as string, viewId as string, { query_data: { ...state.filters, ...property, }, }); - } else if (saveToServer) + } else { + mutateMyViewProps((prevData) => { + if (!prevData) return prevData; + + return { + ...prevData, + view_props: { + ...state, + filters: { + ...state.filters, + ...property, + }, + }, + }; + }, false); + saveDataToServer(workspaceSlug as string, projectId as string, { ...state, filters: { @@ -410,8 +508,20 @@ export const IssueViewContextProvider: React.FC<{ children: React.ReactNode }> = ...property, }, }); + } }, - [projectId, workspaceSlug, state, mutateMyViewProps, viewId, mutateViewDetails] + [ + projectId, + workspaceSlug, + state, + mutateMyViewProps, + cycleId, + mutateCycleDetails, + moduleId, + mutateModuleDetails, + viewId, + mutateViewDetails, + ] ); const setNewDefaultView = useCallback(() => { @@ -439,11 +549,17 @@ export const IssueViewContextProvider: React.FC<{ children: React.ReactNode }> = payload: { ...myViewProps?.view_props, filters: { - ...(viewId ? viewDetails?.query_data : myViewProps?.view_props?.filters), + ...(cycleId + ? cycleDetails?.view_props.filters + : moduleId + ? moduleDetails?.view_props.filters + : viewId + ? viewDetails?.query_data + : myViewProps?.view_props?.filters), } as any, }, }); - }, [myViewProps, viewDetails, viewId]); + }, [myViewProps, cycleId, cycleDetails, moduleId, moduleDetails, viewId, viewDetails]); return ( ): Promise { return this.patch( `/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/`, diff --git a/apps/app/services/modules.service.ts b/apps/app/services/modules.service.ts index 5b7e2623a..89227982c 100644 --- a/apps/app/services/modules.service.ts +++ b/apps/app/services/modules.service.ts @@ -53,7 +53,11 @@ class ProjectIssuesServices extends APIService { }); } - async getModuleDetails(workspaceSlug: string, projectId: string, moduleId: string): Promise { + async getModuleDetails( + workspaceSlug: string, + projectId: string, + moduleId: string + ): Promise { return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/modules/${moduleId}/`) .then((response) => response?.data) .catch((error) => { diff --git a/apps/app/types/cycles.d.ts b/apps/app/types/cycles.d.ts index d39f0b140..760ba03d5 100644 --- a/apps/app/types/cycles.d.ts +++ b/apps/app/types/cycles.d.ts @@ -1,4 +1,12 @@ -import type { IUser, IIssue, IProject, IProjectLite, IWorkspace, IWorkspaceLite } from "types"; +import type { + IUser, + IIssue, + IProject, + IProjectLite, + IWorkspace, + IWorkspaceLite, + IIssueFilterOptions, +} from "types"; export interface ICycle { backlog_issues: number; @@ -21,6 +29,9 @@ export interface ICycle { unstarted_issues: number; updated_at: Date; updated_by: string; + view_props: { + filters: IIssueFilterOptions; + }; workspace: string; workspace_detail: IWorkspaceLite; } diff --git a/apps/app/types/modules.d.ts b/apps/app/types/modules.d.ts index 92f2ae199..3e2a326c4 100644 --- a/apps/app/types/modules.d.ts +++ b/apps/app/types/modules.d.ts @@ -6,6 +6,7 @@ import type { IWorkspace, IWorkspaceLite, IProjectLite, + IIssueFilterOptions, } from "types"; export interface IModule { @@ -45,6 +46,9 @@ export interface IModule { unstarted_issues: number; updated_at: Date; updated_by: string; + view_props: { + filters: IIssueFilterOptions; + }; workspace: string; workspace_detail: IWorkspaceLite; } From ebce364104c3f36925fb8d0ddf6e944037514881 Mon Sep 17 00:00:00 2001 From: Rhea Jain <65884341+rhea0110@users.noreply.github.com> Date: Wed, 19 Apr 2023 20:52:52 +0530 Subject: [PATCH 15/43] chore: changed loading text (#898) --- apps/app/components/onboarding/invite-members.tsx | 2 +- apps/app/layouts/auth-layout/project-authorization-wrapper.tsx | 2 +- .../app/layouts/auth-layout/workspace-authorization-wrapper.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/app/components/onboarding/invite-members.tsx b/apps/app/components/onboarding/invite-members.tsx index 8c257ad9d..e075caf73 100644 --- a/apps/app/components/onboarding/invite-members.tsx +++ b/apps/app/components/onboarding/invite-members.tsx @@ -51,7 +51,7 @@ export const InviteMembers: React.FC = ({ setStep, workspace }) => {
= ({ {loading ? (
-

Setting up your project...

+

Loading your project...

diff --git a/apps/app/layouts/auth-layout/workspace-authorization-wrapper.tsx b/apps/app/layouts/auth-layout/workspace-authorization-wrapper.tsx index 8803653e5..8f9ec7f8b 100644 --- a/apps/app/layouts/auth-layout/workspace-authorization-wrapper.tsx +++ b/apps/app/layouts/auth-layout/workspace-authorization-wrapper.tsx @@ -72,7 +72,7 @@ export const WorkspaceAuthorizationLayout: React.FC = ({ return (
-

Setting up your workspace...

+

Loading your workspace...

From d30a88832af99d51ff601cdce187e99d4960dab3 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Thu, 20 Apr 2023 12:09:35 +0530 Subject: [PATCH 16/43] fix: reset estimates modal form after create/update (#893) --- .../estimates/create-update-estimate-modal.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/app/components/estimates/create-update-estimate-modal.tsx b/apps/app/components/estimates/create-update-estimate-modal.tsx index a8a191eca..92c32a1c2 100644 --- a/apps/app/components/estimates/create-update-estimate-modal.tsx +++ b/apps/app/components/estimates/create-update-estimate-modal.tsx @@ -41,6 +41,11 @@ export const CreateUpdateEstimateModal: React.FC = ({ handleClose, data, defaultValues, }); + const onClose = () => { + handleClose(); + reset(); + }; + const router = useRouter(); const { workspaceSlug, projectId } = router.query; @@ -62,7 +67,6 @@ export const CreateUpdateEstimateModal: React.FC = ({ handleClose, data, (prevData) => [res, ...(prevData ?? [])], false ); - handleClose(); }) .catch(() => { setToastAlert({ @@ -71,6 +75,8 @@ export const CreateUpdateEstimateModal: React.FC = ({ handleClose, data, message: "Error: Estimate could not be created", }); }); + + onClose(); }; const updateEstimate = async (formData: IEstimate) => { @@ -102,7 +108,8 @@ export const CreateUpdateEstimateModal: React.FC = ({ handleClose, data, message: "Error: Estimate could not be updated", }); }); - handleClose(); + + onClose(); }; useEffect(() => { From 7149d206015a2fcbce3b6421cbd0a52c5e65415b Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Thu, 20 Apr 2023 12:09:48 +0530 Subject: [PATCH 17/43] chore: change pages icon (#894) --- .../components/pages/single-page-detailed-item.tsx | 4 ++-- apps/app/components/pages/single-page-list-item.tsx | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/apps/app/components/pages/single-page-detailed-item.tsx b/apps/app/components/pages/single-page-detailed-item.tsx index 0c47e4317..c7ebec25d 100644 --- a/apps/app/components/pages/single-page-detailed-item.tsx +++ b/apps/app/components/pages/single-page-detailed-item.tsx @@ -49,10 +49,10 @@ export const SinglePageDetailedItem: React.FC = ({ return (
- +
-

{truncateText(page.name, 75)}

+

{truncateText(page.name, 75)}

{page.label_details.length > 0 && page.label_details.map((label) => (
= ({
-
- -

+

+ +

{truncateText(page.name, 75)}

{page.label_details.length > 0 && From 4f61c5d552b3f140ddfa8c287139ba79be2044d6 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Thu, 20 Apr 2023 12:09:55 +0530 Subject: [PATCH 18/43] fix: pages access mutation (#896) --- apps/app/components/pages/pages-view.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/app/components/pages/pages-view.tsx b/apps/app/components/pages/pages-view.tsx index c6d600307..7e923b0fe 100644 --- a/apps/app/components/pages/pages-view.tsx +++ b/apps/app/components/pages/pages-view.tsx @@ -166,17 +166,17 @@ export const PagesView: React.FC = ({ pages, viewType }) => { mutate( ALL_PAGES_LIST(projectId as string), - (prevData) => (prevData ?? []).map((p) => ({ ...p, ...formData })), + (prevData) => (prevData ?? []).map((p) => ({ ...p, ...(p.id === page.id ? formData : {}) })), false ); mutate( MY_PAGES_LIST(projectId as string), - (prevData) => (prevData ?? []).map((p) => ({ ...p, ...formData })), + (prevData) => (prevData ?? []).map((p) => ({ ...p, ...(p.id === page.id ? formData : {}) })), false ); mutate( FAVORITE_PAGES_LIST(projectId as string), - (prevData) => (prevData ?? []).map((p) => ({ ...p, ...formData })), + (prevData) => (prevData ?? []).map((p) => ({ ...p, ...(p.id === page.id ? formData : {}) })), false ); @@ -217,7 +217,7 @@ export const PagesView: React.FC = ({ pages, viewType }) => { ))} ) : viewType === "detailed" ? ( -
+
{pages.map((page) => ( Date: Thu, 20 Apr 2023 12:11:33 +0530 Subject: [PATCH 19/43] feat: cycle list tab context (#900) --- .../projects/[projectId]/cycles/index.tsx | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx index bfc8ef002..cd53e80b6 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx @@ -7,6 +7,8 @@ import useSWR from "swr"; // headless ui import { Tab } from "@headlessui/react"; +// hooks +import useLocalStorage from "hooks/use-local-storage"; // services import cycleService from "services/cycles.service"; import projectService from "services/project.service"; @@ -45,6 +47,8 @@ const ProjectCycles: NextPage = () => { const [selectedCycle, setSelectedCycle] = useState(); const [createUpdateCycleModal, setCreateUpdateCycleModal] = useState(false); + const { storedValue: cycleTab, setValue: setCycleTab } = useLocalStorage("cycleTab", "Upcoming"); + const router = useRouter(); const { workspaceSlug, projectId } = router.query; @@ -77,6 +81,20 @@ const ProjectCycles: NextPage = () => { }, 500); }, [createUpdateCycleModal]); + const currentTabValue = (tab: string | null) => { + switch (tab) { + case "Upcoming": + return 0; + case "Completed": + return 1; + case "Drafts": + return 2; + + default: + return 0; + } + }; + return ( { document.dispatchEvent(e); }} > - + Add Cycle } @@ -123,7 +141,22 @@ const ProjectCycles: NextPage = () => {

Other Cycles

- + { + switch (i) { + case 0: + return setCycleTab("Upcoming"); + case 1: + return setCycleTab("Completed"); + case 2: + return setCycleTab("Drafts"); + + default: + return setCycleTab("Upcoming"); + } + }} + > Date: Thu, 20 Apr 2023 12:16:13 +0530 Subject: [PATCH 20/43] chore: upload button and github banner text update (#899) * chore: upload button and github banner text update * chore: attachments button text fix --- apps/app/components/issues/attachment-upload.tsx | 4 ++-- apps/app/pages/[workspaceSlug]/index.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/app/components/issues/attachment-upload.tsx b/apps/app/components/issues/attachment-upload.tsx index 6819807ba..2c6f384ad 100644 --- a/apps/app/components/issues/attachment-upload.tsx +++ b/apps/app/components/issues/attachment-upload.tsx @@ -96,9 +96,9 @@ export const IssueAttachmentUpload = () => { ) : fileError ? (

{fileError}

) : isLoading ? ( -

Uploading....

+

Uploading...

) : ( -

Drag and drop/Click to add

+

Drag and drop here

)}
diff --git a/apps/app/pages/[workspaceSlug]/index.tsx b/apps/app/pages/[workspaceSlug]/index.tsx index 24a6d6a20..0546985f5 100644 --- a/apps/app/pages/[workspaceSlug]/index.tsx +++ b/apps/app/pages/[workspaceSlug]/index.tsx @@ -46,7 +46,7 @@ const WorkspacePage: NextPage = () => { // style={{ background: "linear-gradient(90deg, #8e2de2 0%, #4a00e0 100%)" }} >

- Plane is open source, support us by staring us on GitHub. + Plane is open source, support us by starring us on GitHub.

{/* From 3c2f5d12ed3548d63336c5f05462f0d1cc3c1648 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Thu, 20 Apr 2023 13:41:24 +0530 Subject: [PATCH 21/43] feat: themes (#902) * chore: add next theme and initial setup * chore: add dark mode colors to layouts * chore: user general setting page theming * chore: dashboard theming * chore: project page theming * chore: workspace setting page theming * chore: my issue page theming * chore: cmdk theming * chore: change hardcode bg and text color to theme * chore: change color name according to the design * style: fix card in the dashboard * style: fix merge conflict design issues * style: add light high contrast and dark high contrast * style: fix cmd k menu color and selection * feat: change theme from cmdk menu * chore: add multiple theme field to custom theme * chore: removed custom theming * fix: build error --------- Co-authored-by: Saheb Giri --- .../components/account/email-code-form.tsx | 2 +- .../account/email-password-form.tsx | 2 +- .../account/github-login-button.tsx | 2 +- .../auth-screens/not-authorized-view.tsx | 8 +- apps/app/components/breadcrumbs/index.tsx | 4 +- .../change-interface-theme.tsx | 45 +++ .../command-palette/command-pallette.tsx | 121 ++++---- apps/app/components/command-palette/index.ts | 1 + .../command-palette/shortcuts-modal.tsx | 30 +- .../components/core/board-view/all-boards.tsx | 4 +- .../core/board-view/board-header.tsx | 8 +- .../core/board-view/single-board.tsx | 12 +- .../core/board-view/single-issue.tsx | 10 +- .../core/bulk-delete-issues-modal.tsx | 16 +- .../core/calendar-view/calendar.tsx | 22 +- .../app/components/core/custom-theme-form.tsx | 267 ++++++++++++++++++ .../components/core/custom-theme-modal.tsx | 65 +++++ .../core/existing-issues-list-modal.tsx | 16 +- apps/app/components/core/feeds.tsx | 30 +- apps/app/components/core/filter-list.tsx | 8 +- .../components/core/gpt-assistant-modal.tsx | 2 +- .../components/core/image-picker-popover.tsx | 10 +- .../components/core/image-upload-modal.tsx | 12 +- apps/app/components/core/index.ts | 1 + .../components/core/issues-view-filter.tsx | 45 ++- apps/app/components/core/issues-view.tsx | 4 +- apps/app/components/core/link-modal.tsx | 6 +- .../core/list-view/single-issue.tsx | 2 +- .../components/core/list-view/single-list.tsx | 16 +- .../components/core/sidebar/links-list.tsx | 8 +- .../core/sidebar/sidebar-progress-stats.tsx | 16 +- .../core/sidebar/single-progress-stats.tsx | 4 +- apps/app/components/core/theme-switch.tsx | 60 ++++ .../cycles/completed-cycles-list.tsx | 2 +- apps/app/components/cycles/cycles-list.tsx | 4 +- .../components/cycles/delete-cycle-modal.tsx | 10 +- apps/app/components/cycles/empty-cycle.tsx | 22 +- apps/app/components/cycles/form.tsx | 4 +- apps/app/components/cycles/modal.tsx | 4 +- apps/app/components/cycles/select.tsx | 14 +- apps/app/components/cycles/sidebar.tsx | 42 +-- .../components/cycles/single-cycle-card.tsx | 8 +- .../cycles/transfer-issues-modal.tsx | 14 +- .../app/components/cycles/transfer-issues.tsx | 2 +- .../components/emoji-icon-picker/index.tsx | 12 +- .../integration/single-integration-card.tsx | 2 +- apps/app/components/issues/activity.tsx | 26 +- .../components/issues/comment/add-comment.tsx | 14 +- .../issues/comment/comment-card.tsx | 6 +- .../components/issues/delete-issue-modal.tsx | 6 +- .../components/issues/description-form.tsx | 4 +- apps/app/components/issues/form.tsx | 18 +- apps/app/components/issues/modal.tsx | 4 +- .../components/issues/my-issues-list-item.tsx | 8 +- .../issues/parent-issues-list-modal.tsx | 30 +- .../app/components/issues/select/assignee.tsx | 6 +- apps/app/components/issues/select/date.tsx | 6 +- apps/app/components/issues/select/label.tsx | 32 +-- .../app/components/issues/select/priority.tsx | 4 +- apps/app/components/issues/select/project.tsx | 2 +- apps/app/components/issues/select/state.tsx | 4 +- .../issues/sidebar-select/assignee.tsx | 4 +- .../issues/sidebar-select/blocked.tsx | 22 +- .../issues/sidebar-select/blocker.tsx | 22 +- .../issues/sidebar-select/cycle.tsx | 2 +- .../issues/sidebar-select/module.tsx | 2 +- .../issues/sidebar-select/parent.tsx | 4 +- .../issues/sidebar-select/priority.tsx | 2 +- .../issues/sidebar-select/state.tsx | 2 +- apps/app/components/issues/sidebar.tsx | 38 +-- .../app/components/issues/sub-issues-list.tsx | 8 +- .../issues/view-select/assignee.tsx | 8 +- .../components/issues/view-select/state.tsx | 2 +- .../components/labels/create-label-modal.tsx | 12 +- .../labels/create-update-label-inline.tsx | 8 +- .../components/labels/labels-list-modal.tsx | 14 +- .../components/labels/single-label-group.tsx | 10 +- apps/app/components/labels/single-label.tsx | 4 +- .../modules/delete-module-modal.tsx | 10 +- apps/app/components/modules/form.tsx | 4 +- apps/app/components/modules/modal.tsx | 4 +- .../components/modules/select/select-lead.tsx | 4 +- .../modules/select/select-members.tsx | 8 +- .../modules/sidebar-select/select-lead.tsx | 4 +- .../modules/sidebar-select/select-members.tsx | 4 +- .../modules/sidebar-select/select-status.tsx | 2 +- apps/app/components/modules/sidebar.tsx | 46 +-- .../components/modules/single-module-card.tsx | 16 +- .../components/onboarding/invite-members.tsx | 2 +- .../components/onboarding/user-details.tsx | 4 +- apps/app/components/onboarding/workspace.tsx | 20 +- .../pages/create-update-page-modal.tsx | 4 +- .../components/pages/delete-page-modal.tsx | 10 +- apps/app/components/pages/page-form.tsx | 2 +- apps/app/components/pages/pages-view.tsx | 4 +- .../pages/single-page-detailed-item.tsx | 2 +- .../pages/single-page-list-item.tsx | 4 +- .../project/confirm-project-member-remove.tsx | 10 +- .../project/create-project-modal.tsx | 4 +- .../project/delete-project-modal.tsx | 6 +- .../components/project/join-project-modal.tsx | 6 +- .../project/send-project-invitation-modal.tsx | 10 +- .../project/settings/single-label.tsx | 10 +- apps/app/components/project/sidebar-list.tsx | 4 +- .../project/single-integration-card.tsx | 2 +- .../project/single-project-card.tsx | 4 +- .../project/single-sidebar-project.tsx | 10 +- .../app/components/rich-text-editor/index.tsx | 12 +- .../toolbar/float-tool-tip.tsx | 8 +- .../rich-text-editor/toolbar/link.tsx | 4 +- .../toolbar/table-controls.tsx | 4 +- apps/app/components/search-listbox/index.tsx | 12 +- .../components/states/create-state-modal.tsx | 12 +- .../states/create-update-state-inline.tsx | 2 +- .../components/states/delete-state-modal.tsx | 13 +- apps/app/components/states/single-state.tsx | 10 +- apps/app/components/ui/avatar.tsx | 4 +- .../components/ui/buttons/primary-button.tsx | 6 +- .../ui/buttons/secondary-button.tsx | 8 +- apps/app/components/ui/context-menu.tsx | 6 +- apps/app/components/ui/custom-menu.tsx | 16 +- .../components/ui/custom-search-select.tsx | 22 +- apps/app/components/ui/custom-select.tsx | 12 +- apps/app/components/ui/date.tsx | 10 +- apps/app/components/ui/datepicker.tsx | 6 +- apps/app/components/ui/empty-space.tsx | 16 +- apps/app/components/ui/empty-state.tsx | 6 +- apps/app/components/ui/input/index.tsx | 6 +- apps/app/components/ui/labels-list.tsx | 2 +- apps/app/components/ui/loader.tsx | 2 +- apps/app/components/ui/multi-input.tsx | 4 +- .../components/ui/multi-level-dropdown.tsx | 14 +- apps/app/components/ui/multi-level-select.tsx | 12 +- apps/app/components/ui/outline-button.tsx | 4 +- apps/app/components/ui/text-area/index.tsx | 4 +- apps/app/components/ui/tooltip.tsx | 2 +- .../components/views/delete-view-modal.tsx | 17 +- apps/app/components/views/form.tsx | 2 +- apps/app/components/views/modal.tsx | 4 +- .../components/workspace/activity-graph.tsx | 14 +- .../workspace/completed-issues-graph.tsx | 6 +- .../confirm-workspace-member-remove.tsx | 10 +- .../workspace/create-workspace-form.tsx | 8 +- .../workspace/delete-workspace-modal.tsx | 6 +- .../app/components/workspace/help-section.tsx | 28 +- apps/app/components/workspace/issues-list.tsx | 10 +- .../components/workspace/issues-pie-chart.tsx | 2 +- .../app/components/workspace/issues-stats.tsx | 8 +- .../send-workspace-invitation-modal.tsx | 8 +- .../components/workspace/sidebar-dropdown.tsx | 24 +- .../app/components/workspace/sidebar-menu.tsx | 8 +- .../workspace/single-invitation.tsx | 6 +- apps/app/constants/themes.ts | 35 +++ apps/app/helpers/theme.helper.ts | 35 +++ apps/app/layouts/app-layout/app-header.tsx | 4 +- apps/app/layouts/app-layout/app-sidebar.tsx | 4 +- apps/app/layouts/app-layout/index.tsx | 12 +- .../project-authorization-wrapper.tsx | 2 +- .../workspace-authorization-wrapper.tsx | 6 +- apps/app/layouts/default-layout/index.tsx | 2 +- apps/app/layouts/settings-navbar.tsx | 6 +- apps/app/package.json | 1 + apps/app/pages/404.tsx | 2 +- apps/app/pages/[workspaceSlug]/index.tsx | 6 +- .../pages/[workspaceSlug]/me/my-issues.tsx | 14 +- .../[workspaceSlug]/me/profile/index.tsx | 31 +- .../projects/[projectId]/cycles/[cycleId].tsx | 2 +- .../projects/[projectId]/cycles/index.tsx | 16 +- .../projects/[projectId]/issues/[issueId].tsx | 12 +- .../[projectId]/modules/[moduleId].tsx | 2 +- .../projects/[projectId]/modules/index.tsx | 2 +- .../projects/[projectId]/pages/[pageId].tsx | 12 +- .../projects/[projectId]/pages/index.tsx | 16 +- .../projects/[projectId]/settings/control.tsx | 4 +- .../[projectId]/settings/features.tsx | 32 +-- .../projects/[projectId]/settings/index.tsx | 12 +- .../projects/[projectId]/settings/labels.tsx | 2 +- .../projects/[projectId]/settings/members.tsx | 6 +- .../projects/[projectId]/settings/states.tsx | 8 +- .../projects/[projectId]/views/index.tsx | 8 +- .../pages/[workspaceSlug]/settings/index.tsx | 10 +- .../[workspaceSlug]/settings/members.tsx | 8 +- apps/app/pages/_app.tsx | 23 +- apps/app/pages/_error.js | 6 +- .../pages/installations/[provider]/index.tsx | 4 +- apps/app/pages/signin.tsx | 6 +- .../[invitationId].tsx | 2 +- apps/app/styles/command-pallette.css | 8 +- apps/app/styles/globals.css | 79 +++++- apps/app/tailwind.config.js | 32 +++ yarn.lock | 5 + 191 files changed, 1550 insertions(+), 880 deletions(-) create mode 100644 apps/app/components/command-palette/change-interface-theme.tsx create mode 100644 apps/app/components/core/custom-theme-form.tsx create mode 100644 apps/app/components/core/custom-theme-modal.tsx create mode 100644 apps/app/components/core/theme-switch.tsx create mode 100644 apps/app/constants/themes.ts create mode 100644 apps/app/helpers/theme.helper.ts diff --git a/apps/app/components/account/email-code-form.tsx b/apps/app/components/account/email-code-form.tsx index 5e4c49b1a..389153d60 100644 --- a/apps/app/components/account/email-code-form.tsx +++ b/apps/app/components/account/email-code-form.tsx @@ -141,7 +141,7 @@ export const EmailCodeForm = ({ onSuccess }: any) => { diff --git a/apps/app/components/auth-screens/not-authorized-view.tsx b/apps/app/components/auth-screens/not-authorized-view.tsx index 37c07e8de..054e5bfa3 100644 --- a/apps/app/components/auth-screens/not-authorized-view.tsx +++ b/apps/app/components/auth-screens/not-authorized-view.tsx @@ -36,16 +36,16 @@ export const NotAuthorizedView: React.FC = ({ actionButton, type }) => { alt="ProjectSettingImg" />
-

+

Oops! You are not authorized to view this page

-
+
{user ? (

You have signed in as {user.email}.
- Sign in + Sign in {" "} with different account that has access to this page.

@@ -53,7 +53,7 @@ export const NotAuthorizedView: React.FC = ({ actionButton, type }) => {

You need to{" "} - Sign in + Sign in {" "} with an account that has access to this page.

diff --git a/apps/app/components/breadcrumbs/index.tsx b/apps/app/components/breadcrumbs/index.tsx index 0a2d36e1f..98944fe37 100644 --- a/apps/app/components/breadcrumbs/index.tsx +++ b/apps/app/components/breadcrumbs/index.tsx @@ -16,7 +16,7 @@ const Breadcrumbs = ({ children }: BreadcrumbsProps) => {
-
- +
+ = ({ isOpen, setIsOpen }) => {
-

{shortcut.description}

+

{shortcut.description}

{shortcut.keys.split(",").map((key, index) => ( {key === "Ctrl" ? ( - + ) : ( - + {key === "Ctrl" ? : key} )} @@ -145,7 +145,7 @@ export const ShortcutsModal: React.FC = ({ isOpen, setIsOpen }) => { )) ) : (
-

+

No shortcuts found for{" "} {`"`} @@ -162,16 +162,16 @@ export const ShortcutsModal: React.FC = ({ isOpen, setIsOpen }) => {

{shortcuts.map(({ keys, description }, index) => (
-

{description}

+

{description}

{keys.split(",").map((key, index) => ( {key === "Ctrl" ? ( - + ) : ( - + {key === "Ctrl" ? : key} )} diff --git a/apps/app/components/core/board-view/all-boards.tsx b/apps/app/components/core/board-view/all-boards.tsx index f476b76a5..19d0e7636 100644 --- a/apps/app/components/core/board-view/all-boards.tsx +++ b/apps/app/components/core/board-view/all-boards.tsx @@ -81,7 +81,7 @@ export const AllBoards: React.FC = ({ return (
{currentState && @@ -92,7 +92,7 @@ export const AllBoards: React.FC = ({ : addSpaceIfCamelCase(singleGroup)}
- 0 + 0
); })} diff --git a/apps/app/components/core/board-view/board-header.tsx b/apps/app/components/core/board-view/board-header.tsx index f209ba6e2..93443d99c 100644 --- a/apps/app/components/core/board-view/board-header.tsx +++ b/apps/app/components/core/board-view/board-header.tsx @@ -124,7 +124,7 @@ export const BoardHeader: React.FC = ({ return (
@@ -145,7 +145,7 @@ export const BoardHeader: React.FC = ({ {groupedByIssues?.[groupTitle].length ?? 0} @@ -155,7 +155,7 @@ export const BoardHeader: React.FC = ({
))}
-
+
{monthOptions.map((month) => (
-
+

Show weekends

+
+
+
+
+ Cancel + + {status + ? isSubmitting + ? "Updating Theme..." + : "Update Theme" + : isSubmitting + ? "Creating Theme..." + : "Set Theme"} + +
+ + ); +}; diff --git a/apps/app/components/core/custom-theme-modal.tsx b/apps/app/components/core/custom-theme-modal.tsx new file mode 100644 index 000000000..d46d17d28 --- /dev/null +++ b/apps/app/components/core/custom-theme-modal.tsx @@ -0,0 +1,65 @@ +import React from "react"; + +// headless ui +import { Dialog, Transition } from "@headlessui/react"; +// components +import { ThemeForm } from "./custom-theme-form"; +// helpers +import { applyTheme } from "helpers/theme.helper"; +// fetch-keys + +type Props = { + isOpen: boolean; + handleClose: () => void; +}; + +export const CustomThemeModal: React.FC = ({ isOpen, handleClose }) => { + const onClose = () => { + handleClose(); + }; + + const handleFormSubmit = async (formData: any) => { + applyTheme(formData.palette, formData.darkPalette); + onClose(); + }; + + return ( + + + +
+ + +
+
+ + + + + +
+
+
+
+ ); +}; diff --git a/apps/app/components/core/existing-issues-list-modal.tsx b/apps/app/components/core/existing-issues-list-modal.tsx index 2e255ae19..2d5ac37dc 100644 --- a/apps/app/components/core/existing-issues-list-modal.tsx +++ b/apps/app/components/core/existing-issues-list-modal.tsx @@ -130,7 +130,7 @@ export const ExistingIssuesListModal: React.FC = ({ leaveFrom="opacity-100 scale-100" leaveTo="opacity-0 scale-95" > - +
= ({
)} - +
-

+

Commented {timeAgo(activity.created_at)}

@@ -244,7 +244,7 @@ export const Feeds: React.FC = ({ activities }) => ( editable={false} onBlur={() => ({})} noBorder - customClassName="text-xs bg-gray-100" + customClassName="text-xs bg-brand-surface-1" />
@@ -259,7 +259,7 @@ export const Feeds: React.FC = ({ activities }) => (
{activities.length > 1 && activityIdx !== activities.length - 1 ? (
-
+
{activity.field ? ( activityDetails[activity.field as keyof typeof activityDetails]?.icon ) : activity.actor_detail.avatar && @@ -292,7 +292,7 @@ export const Feeds: React.FC = ({ activities }) => (
-
+
{activity.actor_detail.first_name} {activity.actor_detail.is_bot @@ -300,7 +300,7 @@ export const Feeds: React.FC = ({ activities }) => ( : " " + activity.actor_detail.last_name} {action} - {value} + {value} {timeAgo(activity.created_at)}
diff --git a/apps/app/components/core/filter-list.tsx b/apps/app/components/core/filter-list.tsx index 08d1e8062..c42b7501b 100644 --- a/apps/app/components/core/filter-list.tsx +++ b/apps/app/components/core/filter-list.tsx @@ -57,9 +57,9 @@ export const FilterList: React.FC = ({ filters, setFilters }) => { return (
- + {replaceUnderscoreIfSnakeCase(key)}: {filters[key as keyof IIssueFilterOptions] === null || @@ -131,7 +131,7 @@ export const FilterList: React.FC = ({ filters, setFilters }) => { ? "bg-yellow-100 text-yellow-500 hover:bg-yellow-100" : priority === "low" ? "bg-green-100 text-green-500 hover:bg-green-100" - : "bg-gray-100 text-gray-700 hover:bg-gray-100" + : "bg-brand-surface-1 text-gray-700 hover:bg-brand-surface-1" }`} > {getPriorityIcon(priority)} @@ -339,7 +339,7 @@ export const FilterList: React.FC = ({ filters, setFilters }) => { created_by: null, }) } - className="flex items-center gap-x-1 rounded-full border bg-white px-3 py-1.5 text-xs" + className="flex items-center gap-x-1 rounded-full border border-brand-base bg-brand-surface-2 px-3 py-1.5 text-xs" > Clear all filters diff --git a/apps/app/components/core/gpt-assistant-modal.tsx b/apps/app/components/core/gpt-assistant-modal.tsx index b50b1e0f8..37104e30f 100644 --- a/apps/app/components/core/gpt-assistant-modal.tsx +++ b/apps/app/components/core/gpt-assistant-modal.tsx @@ -121,7 +121,7 @@ export const GptAssistantModal: React.FC = ({ return (
diff --git a/apps/app/components/core/image-picker-popover.tsx b/apps/app/components/core/image-picker-popover.tsx index a6f2efb72..9a3d5ff2f 100644 --- a/apps/app/components/core/image-picker-popover.tsx +++ b/apps/app/components/core/image-picker-popover.tsx @@ -65,7 +65,7 @@ export const ImagePickerPopover: React.FC = ({ label, value, onChange }) return ( setIsOpen((prev) => !prev)} > {label} @@ -79,16 +79,16 @@ export const ImagePickerPopover: React.FC = ({ label, value, onChange }) leaveFrom="transform opacity-100 scale-100" leaveTo="transform opacity-0 scale-95" > - -
+ +
- + {tabOptions.map((tab) => ( `rounded py-1 px-4 text-center text-sm outline-none transition-colors ${ - selected ? "bg-theme text-white" : "text-black" + selected ? "bg-brand-accent text-white" : "text-brand-base" }` } > diff --git a/apps/app/components/core/image-upload-modal.tsx b/apps/app/components/core/image-upload-modal.tsx index 28ff260d4..ba41f3efa 100644 --- a/apps/app/components/core/image-upload-modal.tsx +++ b/apps/app/components/core/image-upload-modal.tsx @@ -110,7 +110,7 @@ export const ImageUploadModal: React.FC = ({ leaveFrom="opacity-100" leaveTo="opacity-0" > -
+
@@ -124,9 +124,9 @@ export const ImageUploadModal: React.FC = ({ leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - +
- + Upload Image
@@ -135,7 +135,7 @@ export const ImageUploadModal: React.FC = ({ {...getRootProps()} className={`relative block h-80 w-full rounded-lg p-12 text-center focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 ${ (image === null && isDragActive) || !value - ? "border-2 border-dashed border-gray-300 hover:border-gray-400" + ? "border-2 border-dashed border-brand-base hover:border-gray-400" : "" }`} > @@ -143,7 +143,7 @@ export const ImageUploadModal: React.FC = ({ <> @@ -157,7 +157,7 @@ export const ImageUploadModal: React.FC = ({ ) : ( <> - + {isDragActive ? "Drop image here to upload" : "Drag & drop image here"} diff --git a/apps/app/components/core/index.ts b/apps/app/components/core/index.ts index 41a71d965..eb547578c 100644 --- a/apps/app/components/core/index.ts +++ b/apps/app/components/core/index.ts @@ -11,3 +11,4 @@ export * from "./link-modal"; export * from "./image-picker-popover"; export * from "./filter-list"; export * from "./feeds"; +export * from "./theme-switch"; diff --git a/apps/app/components/core/issues-view-filter.tsx b/apps/app/components/core/issues-view-filter.tsx index 4b4d15e09..0df8e8f1b 100644 --- a/apps/app/components/core/issues-view-filter.tsx +++ b/apps/app/components/core/issues-view-filter.tsx @@ -12,8 +12,7 @@ import { SelectFilters } from "components/views"; // ui import { CustomMenu } from "components/ui"; // icons -import { ChevronDownIcon, ListBulletIcon, CalendarDaysIcon } from "@heroicons/react/24/outline"; -import { Squares2X2Icon } from "@heroicons/react/20/solid"; +import { ChevronDownIcon, ListBulletIcon, Squares2X2Icon, CalendarDaysIcon } from "@heroicons/react/24/outline"; // helpers import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper"; // types @@ -53,30 +52,30 @@ export const IssuesFilterView: React.FC = () => {
{ {({ open }) => ( <> View @@ -130,11 +129,11 @@ export const IssuesFilterView: React.FC = () => { leaveFrom="opacity-100 translate-y-0" leaveTo="opacity-0 translate-y-1" > - -
+ +
-

Group by

+

Group by

option.key === groupByProperty)?.name ?? @@ -155,7 +154,7 @@ export const IssuesFilterView: React.FC = () => {
-

Order by

+

Order by

option.key === orderBy)?.name ?? @@ -178,7 +177,7 @@ export const IssuesFilterView: React.FC = () => {
-

Issue type

+

Issue type

option.key === filters.type) @@ -201,11 +200,11 @@ export const IssuesFilterView: React.FC = () => {
-

Show empty states

+

Show empty states

-

Display Properties

+

Display Properties

{Object.keys(properties).map((key) => { if (key === "estimate" && !isEstimateActive) return null; diff --git a/apps/app/components/core/issues-view.tsx b/apps/app/components/core/issues-view.tsx index 22261de06..18e685866 100644 --- a/apps/app/components/core/issues-view.tsx +++ b/apps/app/components/core/issues-view.tsx @@ -502,8 +502,8 @@ export const IssuesView: React.FC = ({ title="Create a new issue" description={ - Use
C
shortcut to - create a new issue + Use
C
{" "} + shortcut to create a new issue
} Icon={PlusIcon} diff --git a/apps/app/components/core/link-modal.tsx b/apps/app/components/core/link-modal.tsx index 7fd19dc8a..3c22c2b1b 100644 --- a/apps/app/components/core/link-modal.tsx +++ b/apps/app/components/core/link-modal.tsx @@ -56,7 +56,7 @@ export const LinkModal: React.FC = ({ isOpen, handleClose, onFormSubmit } leaveFrom="opacity-100" leaveTo="opacity-0" > -
+
@@ -70,11 +70,11 @@ export const LinkModal: React.FC = ({ isOpen, handleClose, onFormSubmit } leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - +
- + Add Link
diff --git a/apps/app/components/core/list-view/single-issue.tsx b/apps/app/components/core/list-view/single-issue.tsx index 4fa518769..a728c3a75 100644 --- a/apps/app/components/core/list-view/single-issue.tsx +++ b/apps/app/components/core/list-view/single-issue.tsx @@ -217,7 +217,7 @@ export const SingleListIssue: React.FC = ({
{ e.preventDefault(); setContextMenu(true); diff --git a/apps/app/components/core/list-view/single-list.tsx b/apps/app/components/core/list-view/single-list.tsx index 32880a1a6..bfd8acdab 100644 --- a/apps/app/components/core/list-view/single-list.tsx +++ b/apps/app/components/core/list-view/single-list.tsx @@ -132,10 +132,10 @@ export const SingleList: React.FC = ({ return ( {({ open }) => ( -
+
@@ -144,21 +144,21 @@ export const SingleList: React.FC = ({
{getGroupIcon()}
)} {selectedGroup !== null ? ( -

+

{getGroupTitle()}

) : (

All Issues

)} - - {groupedByIssues?.[groupTitle].length ?? 0} + + {groupedByIssues[groupTitle as keyof IIssue].length}
{type === "issue" ? (
)} - +
{link.title}
-

+

Added {timeAgo(link.created_at)}
by{" "} diff --git a/apps/app/components/core/sidebar/sidebar-progress-stats.tsx b/apps/app/components/core/sidebar/sidebar-progress-stats.tsx index fad29ddaa..84b79e4df 100644 --- a/apps/app/components/core/sidebar/sidebar-progress-stats.tsx +++ b/apps/app/components/core/sidebar/sidebar-progress-stats.tsx @@ -100,13 +100,13 @@ export const SidebarProgressStats: React.FC = ({ > - `w-full rounded px-3 py-1 text-gray-900 ${ - selected ? " bg-theme text-white" : " hover:bg-hover-gray" + `w-full rounded px-3 py-1 text-brand-base ${ + selected ? " bg-brand-accent text-white" : " hover:bg-brand-surface-2" }` } > @@ -114,8 +114,8 @@ export const SidebarProgressStats: React.FC = ({ - `w-full rounded px-3 py-1 text-gray-900 ${ - selected ? " bg-theme text-white" : " hover:bg-hover-gray" + `w-full rounded px-3 py-1 text-brand-base ${ + selected ? " bg-brand-accent text-white" : " hover:bg-brand-surface-2" }` } > @@ -123,8 +123,8 @@ export const SidebarProgressStats: React.FC = ({ - `w-full rounded px-3 py-1 text-gray-900 ${ - selected ? " bg-theme text-white" : " hover:bg-hover-gray" + `w-full rounded px-3 py-1 text-brand-base ${ + selected ? " bg-brand-accent text-white" : " hover:bg-brand-surface-2" }` } > @@ -166,7 +166,7 @@ export const SidebarProgressStats: React.FC = ({ -

+
= ({ }) => (
{title}
diff --git a/apps/app/components/core/theme-switch.tsx b/apps/app/components/core/theme-switch.tsx new file mode 100644 index 000000000..f93b1998e --- /dev/null +++ b/apps/app/components/core/theme-switch.tsx @@ -0,0 +1,60 @@ +import { useState, useEffect, ChangeEvent } from "react"; +import { useTheme } from "next-themes"; +import { THEMES_OBJ } from "constants/themes"; +import { CustomSelect } from "components/ui"; +import { CustomThemeModal } from "./custom-theme-modal"; + +export const ThemeSwitch = () => { + const [mounted, setMounted] = useState(false); + const [customThemeModal, setCustomThemeModal] = useState(false); + const { theme, setTheme } = useTheme(); + + // useEffect only runs on the client, so now we can safely show the UI + useEffect(() => { + setMounted(true); + }, []); + + if (!mounted) { + return null; + } + + return ( + <> + t.value === theme)?.label : "Select your theme"} + onChange={({ value, type }: { value: string; type: string }) => { + if (value === "custom") { + if (!customThemeModal) setCustomThemeModal(true); + } else { + const cssVars = [ + "--color-bg-base", + "--color-bg-surface-1", + "--color-bg-surface-2", + + "--color-border", + "--color-bg-sidebar", + "--color-accent", + + "--color-text-base", + "--color-text-secondary", + ]; + cssVars.forEach((cssVar) => document.documentElement.style.removeProperty(cssVar)); + } + document.documentElement.style.setProperty("color-scheme", type); + setTheme(value); + }} + input + width="w-full" + position="right" + > + {THEMES_OBJ.map(({ value, label, type }) => ( + + {label} + + ))} + + {/* setCustomThemeModal(false)} /> */} + + ); +}; diff --git a/apps/app/components/cycles/completed-cycles-list.tsx b/apps/app/components/cycles/completed-cycles-list.tsx index cb8bb43d7..bf1971368 100644 --- a/apps/app/components/cycles/completed-cycles-list.tsx +++ b/apps/app/components/cycles/completed-cycles-list.tsx @@ -64,7 +64,7 @@ export const CompletedCyclesList: React.FC = ({ {completedCycles ? ( completedCycles.completed_cycles.length > 0 ? (
-
+
Completed cycles are not editable.
diff --git a/apps/app/components/cycles/cycles-list.tsx b/apps/app/components/cycles/cycles-list.tsx index 28cd171d0..68592b051 100644 --- a/apps/app/components/cycles/cycles-list.tsx +++ b/apps/app/components/cycles/cycles-list.tsx @@ -62,8 +62,8 @@ export const CyclesList: React.FC = ({
) : type === "current" ? ( showNoCurrentCycleMessage && ( -
-

No current cycle is present.

+
+

No current cycle is present.

diff --git a/apps/app/components/cycles/delete-cycle-modal.tsx b/apps/app/components/cycles/delete-cycle-modal.tsx index c43d34d68..b69537cdc 100644 --- a/apps/app/components/cycles/delete-cycle-modal.tsx +++ b/apps/app/components/cycles/delete-cycle-modal.tsx @@ -139,7 +139,7 @@ export const DeleteCycleModal: React.FC = ({ leaveFrom="opacity-100" leaveTo="opacity-0" > -
+
@@ -153,8 +153,8 @@ export const DeleteCycleModal: React.FC = ({ leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - -
+ +
= ({ />
- + Delete Cycle
-

+

Are you sure you want to delete cycle-{" "} {data?.name}? All of the data related to the cycle will be permanently removed. This action cannot be undone. diff --git a/apps/app/components/cycles/empty-cycle.tsx b/apps/app/components/cycles/empty-cycle.tsx index dfefa4011..af2f12a11 100644 --- a/apps/app/components/cycles/empty-cycle.tsx +++ b/apps/app/components/cycles/empty-cycle.tsx @@ -37,30 +37,30 @@ export const EmptyCycle = () => { return (

-
+
- Cycle Name + Cycle Name
- - + +
-
+
-
+
- Cycle Name + Cycle Name
- - + +
-
+
@@ -68,7 +68,7 @@ export const EmptyCycle = () => {

Create New Cycle

-

+

Sprint more effectively with Cycles by confining your project
to a fixed amount of time. Create new cycle now.

diff --git a/apps/app/components/cycles/form.tsx b/apps/app/components/cycles/form.tsx index 99e55ce7a..168f4f2cd 100644 --- a/apps/app/components/cycles/form.tsx +++ b/apps/app/components/cycles/form.tsx @@ -94,7 +94,7 @@ export const CycleForm: React.FC = ({ handleFormSubmit, handleClose, stat return (
-

+

{status ? "Update" : "Create"} Cycle

@@ -196,7 +196,7 @@ export const CycleForm: React.FC = ({ handleFormSubmit, handleClose, stat
-
+
Cancel = ({ leaveFrom="opacity-100" leaveTo="opacity-0" > -
+
@@ -164,7 +164,7 @@ export const CreateUpdateCycleModal: React.FC = ({ leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - + = ({ {({ open }) => ( <> - +
{cycles?.find((c) => c.id === value)?.name ?? "Cycles"}
@@ -75,7 +75,7 @@ export const CycleSelect: React.FC = ({ leaveTo="opacity-0" >
{options ? ( @@ -93,7 +93,7 @@ export const CycleSelect: React.FC = ({ : "" } ${ active ? "bg-indigo-50" : "" - } relative cursor-pointer select-none p-2 text-gray-900` + } relative cursor-pointer select-none p-2 text-brand-base` } value={option.value} > @@ -103,14 +103,14 @@ export const CycleSelect: React.FC = ({ )) ) : ( -

No options

+

No options

) ) : ( -

Loading...

+

Loading...

)}
-
- +
+ = ({ isOpen, handleClose }) => filteredOptions.map((option: ICycle) => (
diff --git a/apps/app/components/cycles/transfer-issues.tsx b/apps/app/components/cycles/transfer-issues.tsx index 979e87b93..59a10a4d8 100644 --- a/apps/app/components/cycles/transfer-issues.tsx +++ b/apps/app/components/cycles/transfer-issues.tsx @@ -38,7 +38,7 @@ export const TransferIssues: React.FC = ({ handleClick }) => { : 0; return (
-
+
Completed cycles are not editable.
diff --git a/apps/app/components/emoji-icon-picker/index.tsx b/apps/app/components/emoji-icon-picker/index.tsx index 6c3d3842b..6cb3b84f9 100644 --- a/apps/app/components/emoji-icon-picker/index.tsx +++ b/apps/app/components/emoji-icon-picker/index.tsx @@ -55,7 +55,7 @@ const EmojiIconPicker: React.FC = ({ return ( setIsOpen((prev) => !prev)} > {label} @@ -69,8 +69,8 @@ const EmojiIconPicker: React.FC = ({ leaveFrom="transform opacity-100 scale-100" leaveTo="transform opacity-0 scale-95" > - -
+ +
{tabOptions.map((tab) => ( @@ -100,7 +100,7 @@ const EmojiIconPicker: React.FC = ({ {recentEmojis.map((emoji) => ( +
diff --git a/apps/app/components/issues/comment/comment-card.tsx b/apps/app/components/issues/comment/comment-card.tsx index 9cab65531..4691ad705 100644 --- a/apps/app/components/issues/comment/comment-card.tsx +++ b/apps/app/components/issues/comment/comment-card.tsx @@ -67,7 +67,7 @@ export const CommentCard: React.FC = ({ comment, onSubmit, handleCommentD
)} - +
@@ -77,7 +77,7 @@ export const CommentCard: React.FC = ({ comment, onSubmit, handleCommentD {comment.actor_detail.first_name} {comment.actor_detail.is_bot ? "Bot" : " " + comment.actor_detail.last_name}
-

Commented {timeAgo(comment.created_at)}

+

Commented {timeAgo(comment.created_at)}

{isEditing ? ( @@ -117,7 +117,7 @@ export const CommentCard: React.FC = ({ comment, onSubmit, handleCommentD editable={false} onBlur={() => ({})} noBorder - customClassName="text-xs bg-gray-100" + customClassName="text-xs bg-brand-surface-1" /> )}
diff --git a/apps/app/components/issues/delete-issue-modal.tsx b/apps/app/components/issues/delete-issue-modal.tsx index e161e52a1..4fb5f92ea 100644 --- a/apps/app/components/issues/delete-issue-modal.tsx +++ b/apps/app/components/issues/delete-issue-modal.tsx @@ -88,7 +88,7 @@ export const DeleteIssueModal: React.FC = ({ isOpen, handleClose, data }) leaveFrom="opacity-100" leaveTo="opacity-0" > -
+
@@ -102,7 +102,7 @@ export const DeleteIssueModal: React.FC = ({ isOpen, handleClose, data }) leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - +
@@ -116,7 +116,7 @@ export const DeleteIssueModal: React.FC = ({ isOpen, handleClose, data })
-

+

Are you sure you want to delete issue{" "} {data?.project_detail.identifier}-{data?.sequence_id} diff --git a/apps/app/components/issues/description-form.tsx b/apps/app/components/issues/description-form.tsx index 95962b929..b568fb016 100644 --- a/apps/app/components/issues/description-form.tsx +++ b/apps/app/components/issues/description-form.tsx @@ -115,7 +115,7 @@ export const IssueDescriptionForm: FC = ({ issue, handleFormS role="textbox" /> {characterLimit && ( -

+
255 ? "text-red-500" : "" @@ -158,7 +158,7 @@ export const IssueDescriptionForm: FC = ({ issue, handleFormS )} />
diff --git a/apps/app/components/issues/form.tsx b/apps/app/components/issues/form.tsx index 257a6a4e1..8556c2551 100644 --- a/apps/app/components/issues/form.tsx +++ b/apps/app/components/issues/form.tsx @@ -216,12 +216,12 @@ export const IssueForm: FC = ({ /> )} /> -

+

{status ? "Update" : "Create"} Issue

{watch("parent") && watch("parent") !== "" ? ( -
+
= ({ /> {mostSimilarIssue && (
-

+

@@ -283,7 +283,7 @@ export const IssueForm: FC = ({

-
+
setCreateMore((prevData) => !prevData)} @@ -453,7 +453,7 @@ export const IssueForm: FC = ({
diff --git a/apps/app/components/issues/modal.tsx b/apps/app/components/issues/modal.tsx index 47fe6e871..1b74664b1 100644 --- a/apps/app/components/issues/modal.tsx +++ b/apps/app/components/issues/modal.tsx @@ -219,7 +219,7 @@ export const CreateUpdateIssueModal: React.FC = ({ leaveFrom="opacity-100" leaveTo="opacity-0" > -
+
@@ -233,7 +233,7 @@ export const CreateUpdateIssueModal: React.FC = ({ leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - + = ({ issue, properties, projectId const isNotAllowed = false; return ( -
+
@@ -97,7 +97,7 @@ export const MyIssuesListItem: React.FC = ({ issue, properties, projectId )} - + {truncateText(issue.name, 50)} @@ -127,7 +127,7 @@ export const MyIssuesListItem: React.FC = ({ issue, properties, projectId /> )} {properties.sub_issue_count && ( -
+
{issue?.sub_issues_count} {issue?.sub_issues_count === 1 ? "sub-issue" : "sub-issues"}
)} @@ -136,7 +136,7 @@ export const MyIssuesListItem: React.FC = ({ issue, properties, projectId {issue.label_details.map((label) => ( = ({ leaveFrom="opacity-100 scale-100" leaveTo="opacity-0 scale-95" > - + {multiple ? ( <> ({})} multiple>
  • {query === "" && ( -

    +

    {title}

    )} @@ -182,7 +182,7 @@ export const ParentIssuesListModal: React.FC = ({ value={issue.id} className={({ active }) => `flex cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 ${ - active ? "bg-gray-900 bg-opacity-5 text-gray-900" : "" + active ? "bg-gray-900 bg-opacity-5 text-brand-base" : "" }` } onClick={() => handleClose()} @@ -194,7 +194,7 @@ export const ParentIssuesListModal: React.FC = ({ backgroundColor: issue.state_detail.color, }} /> - + {issue.project_detail?.identifier}-{issue.sequence_id} {" "} {issue.name} @@ -206,9 +206,9 @@ export const ParentIssuesListModal: React.FC = ({ ) : (
    -

    +

    No issues found. Create a new issue with{" "} -
    C
    . +
    C
    .

    )} diff --git a/apps/app/components/issues/select/assignee.tsx b/apps/app/components/issues/select/assignee.tsx index 1d06f3f0e..5c1d7c1b4 100644 --- a/apps/app/components/issues/select/assignee.tsx +++ b/apps/app/components/issues/select/assignee.tsx @@ -54,15 +54,15 @@ export const IssueAssigneeSelect: React.FC = ({ projectId, value = [], on onChange={onChange} options={options} label={ -
    +
    {value && value.length > 0 && Array.isArray(value) ? (
    ) : (
    - - Assignee + + Assignee
    )}
    diff --git a/apps/app/components/issues/select/date.tsx b/apps/app/components/issues/select/date.tsx index 438291a2d..3bb8c1bed 100644 --- a/apps/app/components/issues/select/date.tsx +++ b/apps/app/components/issues/select/date.tsx @@ -18,11 +18,11 @@ export const IssueDateSelect: React.FC = ({ value, onChange }) => ( <> - `flex cursor-pointer items-center rounded-md border text-xs shadow-sm duration-200 + `flex cursor-pointer items-center rounded-md border border-brand-base text-xs shadow-sm duration-200 ${ open - ? "border-theme bg-theme/5 outline-none ring-1 ring-theme " - : "hover:bg-theme/5 " + ? "border-brand-accent bg-brand-accent/5 outline-none ring-1 ring-brand-accent " + : "hover:bg-brand-accent/5 " }` } > diff --git a/apps/app/components/issues/select/label.tsx b/apps/app/components/issues/select/label.tsx index 2adeaa222..f5b023280 100644 --- a/apps/app/components/issues/select/label.tsx +++ b/apps/app/components/issues/select/label.tsx @@ -60,11 +60,11 @@ export const IssueLabelSelect: React.FC = ({ setIsOpen, value, onChange, <> - `flex cursor-pointer items-center rounded-md border text-xs shadow-sm duration-200 + `flex cursor-pointer items-center rounded-md border border-brand-base text-xs shadow-sm duration-200 ${ open - ? "border-theme bg-theme/5 outline-none ring-1 ring-theme " - : "hover:bg-theme/5 " + ? "border-brand-accent bg-brand-accent/5 outline-none ring-1 ring-brand-accent " + : "hover:bg-brand-accent/5 " }` } > @@ -78,8 +78,8 @@ export const IssueLabelSelect: React.FC = ({ setIsOpen, value, onChange, ) : ( - - Label + + Label )} @@ -96,12 +96,12 @@ export const IssueLabelSelect: React.FC = ({ setIsOpen, value, onChange, > -
    - +
    + setQuery(event.target.value)} placeholder="Search for label..." displayValue={(assigned: any) => assigned?.name} @@ -120,7 +120,7 @@ export const IssueLabelSelect: React.FC = ({ setIsOpen, value, onChange, key={label.id} className={({ active }) => `${ - active ? "bg-gray-200" : "" + active ? "bg-brand-surface-2" : "" } group flex min-w-[14rem] cursor-pointer select-none items-center gap-2 truncate rounded px-1 py-1.5 text-gray-600` } value={label.id} @@ -150,8 +150,8 @@ export const IssueLabelSelect: React.FC = ({ setIsOpen, value, onChange, ); } else return ( -
    -
    +
    +
    {label.name}
    @@ -160,7 +160,7 @@ export const IssueLabelSelect: React.FC = ({ setIsOpen, value, onChange, key={child.id} className={({ active }) => `${ - active ? "bg-gray-200" : "" + active ? "bg-brand-surface-2" : "" } group flex min-w-[14rem] cursor-pointer select-none items-center gap-2 truncate rounded px-1 py-1.5 text-gray-600` } value={child.id} @@ -192,14 +192,14 @@ export const IssueLabelSelect: React.FC = ({ setIsOpen, value, onChange, ); }) ) : ( -

    No labels found

    +

    No labels found

    ) ) : ( -

    Loading...

    +

    Loading...

    )}
    -
    +
    = ({ issueDetail?.parent_detail ? ( ) : ( -
    +
    No parent selected
    ) @@ -395,7 +395,7 @@ export const IssueDetailsSidebar: React.FC = ({ return ( { const updatedLabels = watchIssue("labels_list")?.filter( (l) => l !== labelId @@ -435,8 +435,8 @@ export const IssueDetailsSidebar: React.FC = ({ className={`flex ${ isNotAllowed ? "cursor-not-allowed" - : "cursor-pointer hover:bg-gray-100" - } items-center gap-2 rounded-2xl border px-2 py-0.5 text-xs`} + : "cursor-pointer hover:bg-brand-surface-1" + } items-center gap-2 rounded-2xl border border-brand-base px-2 py-0.5 text-xs`} > Select Label @@ -448,7 +448,7 @@ export const IssueDetailsSidebar: React.FC = ({ leaveFrom="opacity-100" leaveTo="opacity-0" > - +
    {issueLabels ? ( issueLabels.length > 0 ? ( @@ -463,9 +463,9 @@ export const IssueDetailsSidebar: React.FC = ({ - `${active || selected ? "bg-indigo-50" : ""} ${ + `${active || selected ? "bg-brand-surface-1" : ""} ${ selected ? "font-medium" : "" - } flex cursor-pointer select-none items-center gap-2 truncate p-2 text-gray-900` + } flex cursor-pointer select-none items-center gap-2 truncate p-2 text-brand-base` } value={label.id} > @@ -483,8 +483,8 @@ export const IssueDetailsSidebar: React.FC = ({ ); } else return ( -
    -
    +
    +
    {" "} {label.name}
    @@ -495,7 +495,7 @@ export const IssueDetailsSidebar: React.FC = ({ className={({ active, selected }) => `${active || selected ? "bg-indigo-50" : ""} ${ selected ? "font-medium" : "" - } flex cursor-pointer select-none items-center gap-2 truncate p-2 text-gray-900` + } flex cursor-pointer select-none items-center gap-2 truncate p-2 text-brand-base` } value={child.id} > @@ -530,8 +530,8 @@ export const IssueDetailsSidebar: React.FC = ({ )} diff --git a/apps/app/components/issues/view-select/assignee.tsx b/apps/app/components/issues/view-select/assignee.tsx index 1429555d9..64199b901 100644 --- a/apps/app/components/issues/view-select/assignee.tsx +++ b/apps/app/components/issues/view-select/assignee.tsx @@ -103,17 +103,17 @@ export const ViewAssigneeSelect: React.FC = ({
    {issue.assignees && issue.assignees.length > 0 && Array.isArray(issue.assignees) ? (
    - {issue.assignees.length} Assignees + {issue.assignees.length} Assignees
    ) : (
    - - Assignee + + Assignee
    )}
    diff --git a/apps/app/components/issues/view-select/state.tsx b/apps/app/components/issues/view-select/state.tsx index 34ce8c82c..4a6482cde 100644 --- a/apps/app/components/issues/view-select/state.tsx +++ b/apps/app/components/issues/view-select/state.tsx @@ -97,7 +97,7 @@ export const ViewStateSelect: React.FC = ({ tooltipHeading="State" tooltipContent={addSpaceIfCamelCase(selectedOption?.name ?? "")} > -
    +
    {selectedOption && getStateGroupIcon(selectedOption.group, "16", "16", selectedOption.color)} {selectedOption?.name ?? "State"} diff --git a/apps/app/components/labels/create-label-modal.tsx b/apps/app/components/labels/create-label-modal.tsx index e07de48f5..c186cbd7c 100644 --- a/apps/app/components/labels/create-label-modal.tsx +++ b/apps/app/components/labels/create-label-modal.tsx @@ -84,7 +84,7 @@ export const CreateLabelModal: React.FC = ({ isOpen, projectId, handleClo leaveFrom="opacity-100" leaveTo="opacity-0" > -
    +
    @@ -98,10 +98,10 @@ export const CreateLabelModal: React.FC = ({ isOpen, projectId, handleClo leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - +
    - + Create Label
    @@ -109,8 +109,8 @@ export const CreateLabelModal: React.FC = ({ isOpen, projectId, handleClo {({ open, close }) => ( <> {watch("color") && watch("color") !== "" && ( @@ -122,7 +122,7 @@ export const CreateLabelModal: React.FC = ({ isOpen, projectId, handleClo /> )}
  • {query === "" && ( -

    +

    Labels

    )} @@ -132,7 +132,7 @@ export const LabelsListModal: React.FC = ({ isOpen, handleClose, parent } }} className={({ active }) => `flex cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 ${ - active ? "bg-gray-900 bg-opacity-5 text-gray-900" : "" + active ? "bg-gray-900 bg-opacity-5 text-brand-base" : "" }` } onClick={() => { @@ -159,10 +159,10 @@ export const LabelsListModal: React.FC = ({ isOpen, handleClose, parent } {query !== "" && filteredLabels.length === 0 && (
    diff --git a/apps/app/components/labels/single-label-group.tsx b/apps/app/components/labels/single-label-group.tsx index d41b8508e..c5e5f91db 100644 --- a/apps/app/components/labels/single-label-group.tsx +++ b/apps/app/components/labels/single-label-group.tsx @@ -66,7 +66,7 @@ export const SingleLabelGroup: React.FC = ({ }; return ( - + {({ open }) => ( <>
    @@ -74,7 +74,7 @@ export const SingleLabelGroup: React.FC = ({ -
    {label.name}
    +
    {label.name}
    @@ -100,7 +100,7 @@ export const SingleLabelGroup: React.FC = ({ @@ -120,9 +120,9 @@ export const SingleLabelGroup: React.FC = ({ {labelChildren.map((child) => (
    -
    +
    = ({ editLabel, handleLabelDelete, }) => ( -
    +
    = ({ backgroundColor: label.color && label.color !== "" ? label.color : "#000", }} /> -
    {label.name}
    +
    {label.name}
    addLabelToGroup(label)}> diff --git a/apps/app/components/modules/delete-module-modal.tsx b/apps/app/components/modules/delete-module-modal.tsx index 70fca1916..77ed8dd4c 100644 --- a/apps/app/components/modules/delete-module-modal.tsx +++ b/apps/app/components/modules/delete-module-modal.tsx @@ -77,7 +77,7 @@ export const DeleteModuleModal: React.FC = ({ isOpen, setIsOpen, data }) leaveFrom="opacity-100" leaveTo="opacity-0" > -
    +
    @@ -91,8 +91,8 @@ export const DeleteModuleModal: React.FC = ({ isOpen, setIsOpen, data }) leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - -
    + +
    = ({ isOpen, setIsOpen, data }) />
    - + Delete Module
    -

    +

    Are you sure you want to delete module-{" "} {data?.name}? All of the data related to the module will be permanently removed. This action cannot be undone. diff --git a/apps/app/components/modules/form.tsx b/apps/app/components/modules/form.tsx index b1e56ce90..fe1e557b7 100644 --- a/apps/app/components/modules/form.tsx +++ b/apps/app/components/modules/form.tsx @@ -66,7 +66,7 @@ export const ModuleForm: React.FC = ({ handleFormSubmit, handleClose, sta return (

    -

    +

    {status ? "Update" : "Create"} Module

    @@ -170,7 +170,7 @@ export const ModuleForm: React.FC = ({ handleFormSubmit, handleClose, sta
    -
    +
    Cancel {status diff --git a/apps/app/components/modules/modal.tsx b/apps/app/components/modules/modal.tsx index b611f415b..d42a09ee7 100644 --- a/apps/app/components/modules/modal.tsx +++ b/apps/app/components/modules/modal.tsx @@ -125,7 +125,7 @@ export const CreateUpdateModuleModal: React.FC = ({ isOpen, setIsOpen, da leaveFrom="opacity-100" leaveTo="opacity-0" > -
    +
    @@ -139,7 +139,7 @@ export const CreateUpdateModuleModal: React.FC = ({ isOpen, setIsOpen, da leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - + = ({ value, onChange }) => { options={options} value={value} label={ -
    +
    {selectedOption ? ( ) : ( -
    +
    No user
    )} diff --git a/apps/app/components/modules/select/select-members.tsx b/apps/app/components/modules/select/select-members.tsx index 439753a91..8d407f36a 100644 --- a/apps/app/components/modules/select/select-members.tsx +++ b/apps/app/components/modules/select/select-members.tsx @@ -51,16 +51,16 @@ export const ModuleMembersSelect: React.FC = ({ value, onChange }) => { +
    {value && value.length > 0 && Array.isArray(value) ? (
    - {value.length} Assignees + {value.length} Assignees
    ) : (
    - - Assignee + + Assignee
    )}
    diff --git a/apps/app/components/modules/sidebar-select/select-lead.tsx b/apps/app/components/modules/sidebar-select/select-lead.tsx index bf45f614e..795d0f75c 100644 --- a/apps/app/components/modules/sidebar-select/select-lead.tsx +++ b/apps/app/components/modules/sidebar-select/select-lead.tsx @@ -62,11 +62,11 @@ export const SidebarLeadSelect: React.FC = ({ value, onChange }) => { +
    {selectedOption ? ( ) : ( -
    +
    = ({ value, onChange }) => { +
    {value && value.length > 0 && Array.isArray(value) ? (
    - {value.length} Assignees + {value.length} Assignees
    ) : ( "No members" diff --git a/apps/app/components/modules/sidebar-select/select-status.tsx b/apps/app/components/modules/sidebar-select/select-status.tsx index 2156b4357..5d5ac9464 100644 --- a/apps/app/components/modules/sidebar-select/select-status.tsx +++ b/apps/app/components/modules/sidebar-select/select-status.tsx @@ -33,7 +33,7 @@ export const SidebarStatusSelect: React.FC = ({ control, submitChanges, w label={ = ({ issues, module, isOpen,
    {module ? ( <> @@ -199,7 +199,7 @@ export const ModuleDetailsSidebar: React.FC = ({ issues, module, isOpen, {capitalizeFirstLetter(`${watch("status")}`)} @@ -218,13 +218,13 @@ export const ModuleDetailsSidebar: React.FC = ({ issues, module, isOpen, )} />
    -
    +
    {({ open }) => ( <> @@ -271,8 +271,8 @@ export const ModuleDetailsSidebar: React.FC = ({ issues, module, isOpen, {({ open }) => ( <> @@ -319,7 +319,7 @@ export const ModuleDetailsSidebar: React.FC = ({ issues, module, isOpen,
    -

    {module.name}

    +

    {module.name}

    setModuleDeleteModal(true)}> @@ -336,7 +336,7 @@ export const ModuleDetailsSidebar: React.FC = ({ issues, module, isOpen,
    - + {module.description}
    @@ -373,7 +373,7 @@ export const ModuleDetailsSidebar: React.FC = ({ issues, module, isOpen, Progress
    -
    +
    = ({ issues, module, isOpen,
    -
    - +
    + {({ open }) => (
    - Progress + Progress {!open && moduleIssues && progressPercentage ? ( {progressPercentage ? `${progressPercentage}%` : ""} @@ -415,7 +415,7 @@ export const ModuleDetailsSidebar: React.FC = ({ issues, module, isOpen, ) : (
    - + Invalid date. Please enter valid date.
    @@ -428,7 +428,7 @@ export const ModuleDetailsSidebar: React.FC = ({ issues, module, isOpen,
    - + Pending Issues -{" "} @@ -437,7 +437,7 @@ export const ModuleDetailsSidebar: React.FC = ({ issues, module, isOpen,
    -
    +
    Ideal @@ -466,15 +466,15 @@ export const ModuleDetailsSidebar: React.FC = ({ issues, module, isOpen,
    -
    - +
    + {({ open }) => (
    - Other Information + Other Information
    {module.total_issues > 0 ? ( @@ -487,7 +487,7 @@ export const ModuleDetailsSidebar: React.FC = ({ issues, module, isOpen, ) : (
    - + No issues found. Please add issue.
    @@ -522,11 +522,11 @@ export const ModuleDetailsSidebar: React.FC = ({ issues, module, isOpen,
    -
    +
    -

    Links

    +

    Links

    diff --git a/apps/app/components/rich-text-editor/toolbar/link.tsx b/apps/app/components/rich-text-editor/toolbar/link.tsx index 458a50171..a47c45c1a 100644 --- a/apps/app/components/rich-text-editor/toolbar/link.tsx +++ b/apps/app/components/rich-text-editor/toolbar/link.tsx @@ -173,12 +173,12 @@ export const FloatingLinkToolbar = () => { return ( <> {!isEditing && ( - + {linkEditButtons} )} {!isEditing && empty && ( - + {linkEditButtons} )} diff --git a/apps/app/components/rich-text-editor/toolbar/table-controls.tsx b/apps/app/components/rich-text-editor/toolbar/table-controls.tsx index 3ad285245..d8f8e24e2 100644 --- a/apps/app/components/rich-text-editor/toolbar/table-controls.tsx +++ b/apps/app/components/rich-text-editor/toolbar/table-controls.tsx @@ -8,7 +8,7 @@ export const TableControls = () => {
    diff --git a/apps/app/components/states/create-state-modal.tsx b/apps/app/components/states/create-state-modal.tsx index ca19cd752..769ca6821 100644 --- a/apps/app/components/states/create-state-modal.tsx +++ b/apps/app/components/states/create-state-modal.tsx @@ -102,7 +102,7 @@ export const CreateStateModal: React.FC = ({ isOpen, projectId, handleClo leaveFrom="opacity-100" leaveTo="opacity-0" > -
    +
    @@ -116,10 +116,10 @@ export const CreateStateModal: React.FC = ({ isOpen, projectId, handleClo leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - +
    - + Create State
    @@ -165,8 +165,8 @@ export const CreateStateModal: React.FC = ({ isOpen, projectId, handleClo {({ open }) => ( <> Color @@ -179,7 +179,7 @@ export const CreateStateModal: React.FC = ({ isOpen, projectId, handleClo /> )}
    diff --git a/apps/app/components/states/delete-state-modal.tsx b/apps/app/components/states/delete-state-modal.tsx index d1eca3a93..362ff9353 100644 --- a/apps/app/components/states/delete-state-modal.tsx +++ b/apps/app/components/states/delete-state-modal.tsx @@ -90,7 +90,7 @@ export const DeleteStateModal: React.FC = ({ isOpen, onClose, data }) => leaveFrom="opacity-100" leaveTo="opacity-0" > -
    +
    @@ -104,8 +104,8 @@ export const DeleteStateModal: React.FC = ({ isOpen, onClose, data }) => leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - -
    + +
    = ({ isOpen, onClose, data }) => />
    - + Delete State
    -

    +

    Are you sure you want to delete state-{" "} {data?.name}? All of the data related to the state will be permanently removed. This action cannot be undone. diff --git a/apps/app/components/states/single-state.tsx b/apps/app/components/states/single-state.tsx index f4383b376..2a65a1674 100644 --- a/apps/app/components/states/single-state.tsx +++ b/apps/app/components/states/single-state.tsx @@ -135,12 +135,12 @@ export const SingleState: React.FC = ({ return (

    {getStateGroupIcon(state.group, "20", "20", state.color)}
    -
    {addSpaceIfCamelCase(state.name)}
    +
    {addSpaceIfCamelCase(state.name)}

    {state.description}

    @@ -148,7 +148,7 @@ export const SingleState: React.FC = ({ {index !== 0 && ( diff --git a/apps/app/components/ui/multi-level-select.tsx b/apps/app/components/ui/multi-level-select.tsx index 1662c58a8..a063ee45f 100644 --- a/apps/app/components/ui/multi-level-select.tsx +++ b/apps/app/components/ui/multi-level-select.tsx @@ -48,7 +48,7 @@ export const MultiLevelSelect: React.FC = ({
    setOpenChildFor(null)} - className="relative w-full cursor-default rounded-lg bg-white py-2 pl-3 pr-10 text-left shadow-md sm:text-sm" + className="relative w-full cursor-default rounded-lg bg-brand-surface-2 py-2 pl-3 pr-10 text-left shadow-md sm:text-sm" > {selected?.label ?? label} @@ -64,13 +64,13 @@ export const MultiLevelSelect: React.FC = ({ > {options.map((option) => ( { if (option.children !== null) { @@ -88,7 +88,7 @@ export const MultiLevelSelect: React.FC = ({ <> {openChildFor?.id === option.id && (
    = ({ = ({ ))}
    ( theme === "primary" ? `${ disabled ? "opacity-70" : "" - } border border-theme text-white shadow-sm hover:bg-theme focus:outline-none` + } border border-brand-accent text-white shadow-sm hover:bg-brand-accent focus:outline-none` : theme === "secondary" - ? "border bg-transparent hover:bg-gray-100" + ? "border bg-transparent hover:bg-brand-surface-1" : theme === "success" ? `${ disabled ? "opacity-70" : "" diff --git a/apps/app/components/ui/text-area/index.tsx b/apps/app/components/ui/text-area/index.tsx index 8cda29077..be4938699 100644 --- a/apps/app/components/ui/text-area/index.tsx +++ b/apps/app/components/ui/text-area/index.tsx @@ -44,7 +44,7 @@ export const TextArea: React.FC = ({ return ( <> {label && ( -
    -
    +

    Pending issues

    {data ? ( @@ -52,7 +52,7 @@ export const IssuesStats: React.FC = ({ data }) => ( )}
    -
    +

    Issues due by this week

    {data ? ( diff --git a/apps/app/components/workspace/send-workspace-invitation-modal.tsx b/apps/app/components/workspace/send-workspace-invitation-modal.tsx index b81023c2a..f58269b89 100644 --- a/apps/app/components/workspace/send-workspace-invitation-modal.tsx +++ b/apps/app/components/workspace/send-workspace-invitation-modal.tsx @@ -83,7 +83,7 @@ const SendWorkspaceInvitationModal: React.FC = ({ leaveFrom="opacity-100" leaveTo="opacity-0" > -
    +
    @@ -97,14 +97,14 @@ const SendWorkspaceInvitationModal: React.FC = ({ leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - +
    - + Members -

    +

    Invite members to work on your workspace.

    diff --git a/apps/app/components/workspace/sidebar-dropdown.tsx b/apps/app/components/workspace/sidebar-dropdown.tsx index 93be576fe..ac5ee7f7a 100644 --- a/apps/app/components/workspace/sidebar-dropdown.tsx +++ b/apps/app/components/workspace/sidebar-dropdown.tsx @@ -84,13 +84,13 @@ export const WorkspaceSidebarDropdown = () => {
    - +
    -
    +
    {activeWorkspace?.logo && activeWorkspace.logo !== "" ? ( { >
    -
    {user?.email}
    - Workspace +
    {user?.email}
    + Workspace {workspaces ? (
    {workspaces.length > 0 ? ( @@ -148,7 +148,7 @@ export const WorkspaceSidebarDropdown = () => {
    )}
    -
    +
    {userLinks(workspaceSlug as string).map((link, index) => ( {link.name} @@ -217,11 +217,11 @@ export const WorkspaceSidebarDropdown = () => { ))}
    -
    diff --git a/apps/app/constants/themes.ts b/apps/app/constants/themes.ts new file mode 100644 index 000000000..8ac745b82 --- /dev/null +++ b/apps/app/constants/themes.ts @@ -0,0 +1,35 @@ +export const THEMES = [ + "light", + "dark", + "light-contrast", + "dark-contrast", + // "custom" +]; + +export const THEMES_OBJ = [ + { + value: "light", + label: "Light", + type: "light", + }, + { + value: "dark", + label: "Dark", + type: "dark", + }, + { + value: "light-contrast", + label: "Light High Contrast", + type: "light", + }, + { + value: "dark-contrast", + label: "Dark High Contrast", + type: "dark", + }, + // { + // value: "custom", + // label: "Custom", + // type: "light", + // }, +]; diff --git a/apps/app/helpers/theme.helper.ts b/apps/app/helpers/theme.helper.ts new file mode 100644 index 000000000..d3d76f3e5 --- /dev/null +++ b/apps/app/helpers/theme.helper.ts @@ -0,0 +1,35 @@ +export const hexToRgb = (hex: string) => { + hex = hex.toLowerCase(); + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result + ? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)].join(",") + : null; +}; + +export const applyTheme = (palette: string, isDarkPalette: boolean) => { + const values: string[] = []; + palette.split(",").map((color: string) => { + const cssVarColor = hexToRgb(color); + if (cssVarColor) values.push(cssVarColor); + }); + + const cssVars = [ + "--color-bg-base", + "--color-bg-surface-1", + "--color-bg-surface-2", + "--color-border", + "--color-bg-sidebar", + "--color-accent", + "--color-text-base", + "--color-text-secondary", + "color-scheme", + ]; + + values.push(isDarkPalette ? "dark" : "light"); + + cssVars.forEach((cssVar, i) => + document + .querySelector("[data-theme='custom']") + ?.style.setProperty(cssVar, values[i]) + ); +}; diff --git a/apps/app/layouts/app-layout/app-header.tsx b/apps/app/layouts/app-layout/app-header.tsx index 13106f480..81ec5dbf0 100644 --- a/apps/app/layouts/app-layout/app-header.tsx +++ b/apps/app/layouts/app-layout/app-header.tsx @@ -9,12 +9,12 @@ type Props = { }; const Header: React.FC = ({ breadcrumbs, left, right, setToggleSidebar }) => ( -
    +
    @@ -375,7 +375,7 @@ const SinglePage: NextPage = () => { customButton={ {/*
    -
    +

    Modules

    -

    +

    Modules are enabled for all the projects in this workspace. Access it from the navigation bar.

    @@ -146,7 +146,7 @@ const FeaturesSettings: NextPage = () => {
    -
    +

    Views

    -

    +

    Views are enabled for all the projects in this workspace. Access it from the navigation bar.

    @@ -187,7 +187,7 @@ const FeaturesSettings: NextPage = () => {
    -
    +

    Pages

    -

    +

    Pages are enabled for all the projects in this workspace. Access them from the navigation bar.

    @@ -228,7 +228,7 @@ const FeaturesSettings: NextPage = () => {
    -
    +
    {key === activeGroup && ( { diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/views/index.tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/views/index.tsx index 7c8f7ff64..e6a7eb10a 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/views/index.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/views/index.tsx @@ -79,7 +79,7 @@ const ProjectViews: NextPage = () => { document.dispatchEvent(e); }} > - + Create View
    @@ -98,8 +98,8 @@ const ProjectViews: NextPage = () => { {views ? ( views.length > 0 ? (
    -

    Views

    -
      +

      Views

      +
      {views.map((view) => ( { handleDeleteView={() => handleDeleteView(view)} /> ))} -
    +
    ) : ( {

    Logo

    -

    +

    Max file size is 5MB. Supported file types are .jpg and .png.

    @@ -228,7 +228,7 @@ const WorkspaceSettings: NextPage = () => {

    URL

    -

    Your workspace URL.

    +

    Your workspace URL.

    {

    Name

    -

    Give a name to your workspace.

    +

    Give a name to your workspace.

    {

    Company Size

    -

    How big is your company?

    +

    How big is your company?

    {

    Danger Zone

    -

    +

    The danger zone of the workspace delete page is a critical area that requires careful consideration and attention. When deleting a workspace, all of the data and resources within that workspace will be permanently removed and cannot be recovered. diff --git a/apps/app/pages/[workspaceSlug]/settings/members.tsx b/apps/app/pages/[workspaceSlug]/settings/members.tsx index a85c00cf3..f993c2ff9 100644 --- a/apps/app/pages/[workspaceSlug]/settings/members.tsx +++ b/apps/app/pages/[workspaceSlug]/settings/members.tsx @@ -142,7 +142,7 @@ const MembersSettings: NextPage = () => {

    Members

    @@ -424,7 +429,7 @@ export const CalendarView = () => { key={index} ref={provided.innerRef} {...provided.droppableProps} - className={`flex flex-col gap-1.5 border-t border-brand-base p-2.5 text-left text-sm font-medium hover:bg-brand-surface-1 ${ + className={`group flex flex-col gap-1.5 border-t border-brand-base p-2.5 text-left text-sm font-medium hover:bg-brand-surface-1 ${ showWeekEnds ? (index + 1) % 7 === 0 ? "" @@ -458,6 +463,15 @@ export const CalendarView = () => { )} ))} +
    + +
    {provided.placeholder}
    )} diff --git a/apps/app/components/core/issues-view.tsx b/apps/app/components/core/issues-view.tsx index 18e685866..6c80945c9 100644 --- a/apps/app/components/core/issues-view.tsx +++ b/apps/app/components/core/issues-view.tsx @@ -280,6 +280,17 @@ export const IssuesView: React.FC = ({ [setCreateIssueModal, setPreloadedData, selectedGroup] ); + const addIssueToDate = useCallback( + (date: string) => { + setCreateIssueModal(true); + setPreloadedData({ + target_date: date, + actionType: "createIssue", + }); + }, + [setCreateIssueModal, setPreloadedData] + ); + const makeIssueCopy = useCallback( (issue: IIssue) => { setCreateIssueModal(true); @@ -389,7 +400,7 @@ export const IssuesView: React.FC = ({ <>
    @@ -481,7 +492,7 @@ export const IssuesView: React.FC = ({ userAuth={memberRole} /> ) : ( - + )} ) : type === "issue" ? ( From 170b3d6eecc745adee28ab2891de018c48f803f4 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Thu, 20 Apr 2023 18:09:01 +0530 Subject: [PATCH 25/43] fix: cycle and module sidebar fix (#905) --- apps/app/components/cycles/sidebar.tsx | 4 ++-- apps/app/components/modules/sidebar.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/app/components/cycles/sidebar.tsx b/apps/app/components/cycles/sidebar.tsx index 1b9a3de34..bbbd170e4 100644 --- a/apps/app/components/cycles/sidebar.tsx +++ b/apps/app/components/cycles/sidebar.tsx @@ -345,7 +345,7 @@ export const CycleDetailsSidebar: React.FC = ({
    - + {({ open }) => (
    = ({
    - + {({ open }) => (
    = ({ issues, module, isOpen,
    - + {({ open }) => (
    = ({ issues, module, isOpen,
    - + {({ open }) => (
    Date: Thu, 20 Apr 2023 18:13:21 +0530 Subject: [PATCH 26/43] style: list and kanban view theming (#906) * fix: sidebar workspace dropdown logo * style: list and kanban view theming --- .../core/board-view/board-header.tsx | 6 +- .../core/board-view/single-issue.tsx | 8 +- .../components/core/issues-view-filter.tsx | 9 +- .../core/list-view/single-issue.tsx | 20 +-- .../components/core/list-view/single-list.tsx | 18 +-- .../issues/view-select/assignee.tsx | 2 +- .../issues/view-select/priority.tsx | 16 +- apps/app/components/ui/avatar.tsx | 36 +++-- apps/app/components/ui/custom-menu.tsx | 142 +++++++++--------- .../components/ui/custom-search-select.tsx | 10 +- apps/app/components/ui/custom-select.tsx | 4 +- apps/app/components/ui/datepicker.tsx | 4 +- .../components/ui/multi-level-dropdown.tsx | 10 +- apps/app/components/views/select-filters.tsx | 2 +- .../project-authorization-wrapper.tsx | 13 +- apps/app/styles/globals.css | 6 +- apps/app/tailwind.config.js | 8 +- 17 files changed, 170 insertions(+), 144 deletions(-) diff --git a/apps/app/components/core/board-view/board-header.tsx b/apps/app/components/core/board-view/board-header.tsx index 93443d99c..6fa8f68f3 100644 --- a/apps/app/components/core/board-view/board-header.tsx +++ b/apps/app/components/core/board-view/board-header.tsx @@ -123,8 +123,8 @@ export const BoardHeader: React.FC = ({ return (
    @@ -145,7 +145,7 @@ export const BoardHeader: React.FC = ({ {groupedByIssues?.[groupTitle].length ?? 0} diff --git a/apps/app/components/core/board-view/single-issue.tsx b/apps/app/components/core/board-view/single-issue.tsx index 76ae934d7..453a3bc19 100644 --- a/apps/app/components/core/board-view/single-issue.tsx +++ b/apps/app/components/core/board-view/single-issue.tsx @@ -358,7 +358,7 @@ export const SingleBoardIssue: React.FC = ({ {issue.label_details.map((label) => (
    = ({ /> )} {properties.link && ( -
    +
    @@ -399,10 +399,10 @@ export const SingleBoardIssue: React.FC = ({
    )} {properties.attachment_count && ( -
    +
    - + {issue.attachment_count}
    diff --git a/apps/app/components/core/issues-view-filter.tsx b/apps/app/components/core/issues-view-filter.tsx index 0df8e8f1b..cbabb23a1 100644 --- a/apps/app/components/core/issues-view-filter.tsx +++ b/apps/app/components/core/issues-view-filter.tsx @@ -12,7 +12,12 @@ import { SelectFilters } from "components/views"; // ui import { CustomMenu } from "components/ui"; // icons -import { ChevronDownIcon, ListBulletIcon, Squares2X2Icon, CalendarDaysIcon } from "@heroicons/react/24/outline"; +import { + ChevronDownIcon, + ListBulletIcon, + Squares2X2Icon, + CalendarDaysIcon, +} from "@heroicons/react/24/outline"; // helpers import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper"; // types @@ -113,7 +118,7 @@ export const IssuesFilterView: React.FC = () => { <> View diff --git a/apps/app/components/core/list-view/single-issue.tsx b/apps/app/components/core/list-view/single-issue.tsx index a728c3a75..529adeeaf 100644 --- a/apps/app/components/core/list-view/single-issue.tsx +++ b/apps/app/components/core/list-view/single-issue.tsx @@ -217,7 +217,7 @@ export const SingleListIssue: React.FC = ({
    { e.preventDefault(); setContextMenu(true); @@ -231,13 +231,15 @@ export const SingleListIssue: React.FC = ({ tooltipHeading="Issue ID" tooltipContent={`${issue.project_detail?.identifier}-${issue.sequence_id}`} > - + {issue.project_detail?.identifier}-{issue.sequence_id} )} - {truncateText(issue.name, 50)} + + {truncateText(issue.name, 50)} + @@ -267,7 +269,7 @@ export const SingleListIssue: React.FC = ({ /> )} {properties.sub_issue_count && ( -
    +
    {issue.sub_issues_count} {issue.sub_issues_count === 1 ? "sub-issue" : "sub-issues"}
    )} @@ -276,7 +278,7 @@ export const SingleListIssue: React.FC = ({ {issue.label_details.map((label) => ( = ({ /> )} {properties.link && ( -
    - +
    +
    {issue.link_count} @@ -318,8 +320,8 @@ export const SingleListIssue: React.FC = ({
    )} {properties.attachment_count && ( -
    - +
    +
    {issue.attachment_count} diff --git a/apps/app/components/core/list-view/single-list.tsx b/apps/app/components/core/list-view/single-list.tsx index bfd8acdab..5e71063a3 100644 --- a/apps/app/components/core/list-view/single-list.tsx +++ b/apps/app/components/core/list-view/single-list.tsx @@ -130,14 +130,10 @@ export const SingleList: React.FC = ({ }; return ( - + {({ open }) => ( -
    -
    +
    +
    {selectedGroup !== null && ( @@ -150,7 +146,7 @@ export const SingleList: React.FC = ({ ) : (

    All Issues

    )} - + {groupedByIssues[groupTitle as keyof IIssue].length}
    @@ -168,7 +164,7 @@ export const SingleList: React.FC = ({ ) : ( +
    } @@ -215,7 +211,9 @@ export const SingleList: React.FC = ({ /> )) ) : ( -

    No issues.

    +

    + No issues. +

    ) ) : (
    Loading...
    diff --git a/apps/app/components/issues/view-select/assignee.tsx b/apps/app/components/issues/view-select/assignee.tsx index 64199b901..188543185 100644 --- a/apps/app/components/issues/view-select/assignee.tsx +++ b/apps/app/components/issues/view-select/assignee.tsx @@ -106,7 +106,7 @@ export const ViewAssigneeSelect: React.FC = ({ } items-center gap-2 text-brand-secondary`} > {issue.assignees && issue.assignees.length > 0 && Array.isArray(issue.assignees) ? ( -
    +
    {issue.assignees.length} Assignees
    diff --git a/apps/app/components/issues/view-select/priority.tsx b/apps/app/components/issues/view-select/priority.tsx index 11cd4e698..fe6fb189e 100644 --- a/apps/app/components/issues/view-select/priority.tsx +++ b/apps/app/components/issues/view-select/priority.tsx @@ -52,19 +52,19 @@ export const ViewPrioritySelect: React.FC = ({ customButton={
    ); @@ -141,7 +147,7 @@ const MenuItem: React.FC = ({ {children} @@ -152,7 +158,7 @@ const MenuItem: React.FC = ({ type="button" className={`${className} ${ active ? "bg-brand-surface-1" : "" - } w-full select-none gap-2 truncate rounded px-1 py-1.5 text-left text-brand-secondary hover:bg-brand-surface-1 hover:text-brand-muted-1`} + } hover:text-brand-muted-1 w-full select-none gap-2 truncate rounded px-1 py-1.5 text-left text-brand-secondary hover:bg-brand-surface-1`} onClick={onClick} > {children} diff --git a/apps/app/components/ui/custom-search-select.tsx b/apps/app/components/ui/custom-search-select.tsx index d6e3d3396..8daeed336 100644 --- a/apps/app/components/ui/custom-search-select.tsx +++ b/apps/app/components/ui/custom-search-select.tsx @@ -68,11 +68,11 @@ export const CustomSearchSelect = ({ {customButton} ) : ( `${active || selected ? "bg-brand-surface-1" : ""} ${ selected ? "font-medium" : "" - } text-brand-muted-1 flex cursor-pointer select-none items-center justify-between gap-2 truncate rounded px-1 py-1.5` + } flex cursor-pointer select-none items-center justify-between gap-2 truncate rounded px-1 py-1.5 text-brand-secondary` } > {({ active, selected }) => ( diff --git a/apps/app/components/ui/custom-select.tsx b/apps/app/components/ui/custom-select.tsx index 62a87ec45..fd2a3b8b1 100644 --- a/apps/app/components/ui/custom-select.tsx +++ b/apps/app/components/ui/custom-select.tsx @@ -51,8 +51,8 @@ const CustomSelect = ({ ) : ( = ({ }} className={`${ renderAs === "input" - ? "block border-brand-base bg-transparent px-3 py-2 text-sm focus:outline-none" + ? "block px-3 py-2 text-sm focus:outline-none" : renderAs === "button" ? `px-3 py-1 text-xs shadow-sm ${ disabled ? "" : "hover:bg-brand-surface-1" @@ -44,7 +44,7 @@ export const CustomDatePicker: React.FC = ({ : "" } ${error ? "border-red-500 bg-red-100" : ""} ${ disabled ? "cursor-not-allowed" : "cursor-pointer" - } w-full rounded-md border bg-transparent caret-transparent ${className}`} + } w-full rounded-md border border-brand-base bg-transparent caret-transparent ${className}`} dateFormat="dd-MM-yyyy" isClearable={isClearable} disabled={disabled} diff --git a/apps/app/components/ui/multi-level-dropdown.tsx b/apps/app/components/ui/multi-level-dropdown.tsx index 2b2a9719b..8309b4441 100644 --- a/apps/app/components/ui/multi-level-dropdown.tsx +++ b/apps/app/components/ui/multi-level-dropdown.tsx @@ -41,7 +41,7 @@ export const MultiLevelDropdown: React.FC = ({
    setOpenChildFor(null)} - className={`group flex items-center justify-between gap-2 rounded-md border border-brand-base px-3 py-1.5 text-xs shadow-sm duration-300 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${ + className={`group flex items-center justify-between gap-2 rounded-md border border-brand-base px-3 py-1.5 text-xs shadow-sm duration-300 focus:outline-none ${ open ? "bg-brand-surface-1 text-brand-base" : "text-brand-secondary" }`} > @@ -83,8 +83,8 @@ export const MultiLevelDropdown: React.FC = ({ <>
    @@ -126,8 +126,8 @@ export const MultiLevelDropdown: React.FC = ({ onSelect(child.value); }} className={`${ - child.selected ? "bg-brand-surface-1" : "text-brand-base" - } flex w-full items-center break-all rounded px-1 py-1.5 text-left capitalize hover:bg-brand-surface-1`} + child.selected ? "bg-brand-surface-1" : "" + } flex w-full items-center break-all rounded px-1 py-1.5 text-left capitalize text-brand-secondary hover:bg-brand-surface-1`} > {child.label} diff --git a/apps/app/components/views/select-filters.tsx b/apps/app/components/views/select-filters.tsx index 732b115c2..2d25983c7 100644 --- a/apps/app/components/views/select-filters.tsx +++ b/apps/app/components/views/select-filters.tsx @@ -160,7 +160,7 @@ export const SelectFilters: React.FC = ({ label: (
    = ({
    {loading ? ( -
    +

    Loading your project...

    @@ -81,8 +80,8 @@ const ProjectAuthorizationWrapped: React.FC = ({ ) : error?.status === 401 || error?.status === 403 ? ( ) : error?.status === 404 ? ( -
    -
    +
    +

    No such project exist. Create one?

    { @@ -121,7 +120,11 @@ const ProjectAuthorizationWrapped: React.FC = ({ className={`flex w-full flex-grow flex-col ${ noPadding || issueView === "list" ? "" : settingsLayout ? "p-8 lg:px-28" : "p-8" } ${ - bg === "primary" ? "bg-brand-base" : bg === "secondary" ? "bg-brand-surface-1" : "bg-brand-base" + bg === "primary" + ? "bg-brand-base" + : bg === "secondary" + ? "bg-brand-surface-1" + : "bg-brand-base" }`} > {settingsLayout && ( diff --git a/apps/app/styles/globals.css b/apps/app/styles/globals.css index afd3d59b9..7e4137273 100644 --- a/apps/app/styles/globals.css +++ b/apps/app/styles/globals.css @@ -113,12 +113,11 @@ .horizontal-scroll-enable::-webkit-scrollbar-track { height: 7px; - background-color: #f5f5f5; } .horizontal-scroll-enable::-webkit-scrollbar-thumb { border-radius: 5px; - background-color: #9ca3af; + background-color: rgba(var(--color-bg-surface-1)); } .vertical-scroll-enable::-webkit-scrollbar { @@ -128,12 +127,11 @@ .vertical-scroll-enable::-webkit-scrollbar-track { width: 5px; - background-color: #f5f5f5; } .vertical-scroll-enable::-webkit-scrollbar-thumb { border-radius: 5px; - background-color: #9ca3af; + background-color: rgba(var(--color-bg-surface-1)); } /* end scrollbar style */ diff --git a/apps/app/tailwind.config.js b/apps/app/tailwind.config.js index 85c18cda1..ddb9fb4ad 100644 --- a/apps/app/tailwind.config.js +++ b/apps/app/tailwind.config.js @@ -1,8 +1,7 @@ function withOpacity(variableName) { return ({ opacityValue }) => { - if (opacityValue !== undefined) { - return `rgba(var(${variableName}), ${opacityValue})`; - } + if (opacityValue !== undefined) return `rgba(var(${variableName}), ${opacityValue})`; + return `rgb(var(${variableName}))`; }; } @@ -19,11 +18,14 @@ module.exports = { secondary: "white", brand: { accent: withOpacity("--color-accent"), + base: withOpacity("--color-border"), }, }, borderColor: { brand: { base: withOpacity("--color-border"), + "surface-1": withOpacity("--color-bg-surface-1"), + "surface-2": withOpacity("--color-bg-surface-2"), }, }, backgroundColor: { From 73a8bbb31f1a5288c5551100b43481bbd9a609c9 Mon Sep 17 00:00:00 2001 From: pablohashescobar <118773738+pablohashescobar@users.noreply.github.com> Date: Thu, 20 Apr 2023 18:13:49 +0530 Subject: [PATCH 27/43] feat: release information endpoint (#876) * dev: init release notes * feat: API endpoint to fetch get last 5 release information --- apiserver/plane/api/urls.py | 10 +++++++++ apiserver/plane/api/views/__init__.py | 3 +++ apiserver/plane/api/views/release.py | 21 +++++++++++++++++++ apiserver/plane/settings/local.py | 3 +++ apiserver/plane/settings/production.py | 6 +++++- apiserver/plane/settings/staging.py | 4 +++- apiserver/plane/utils/integrations/github.py | 22 ++++++++++++++++++++ 7 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 apiserver/plane/api/views/release.py diff --git a/apiserver/plane/api/urls.py b/apiserver/plane/api/urls.py index 0e27ce665..029358c98 100644 --- a/apiserver/plane/api/urls.py +++ b/apiserver/plane/api/urls.py @@ -146,6 +146,9 @@ from plane.api.views import ( # Gpt GPTIntegrationEndpoint, ## End Gpt + # Release Notes + ReleaseNotesEndpoint, + ## End Release Notes ) @@ -1284,4 +1287,11 @@ urlpatterns = [ name="importer", ), ## End Gpt + # Release Notes + path( + "release-notes/", + ReleaseNotesEndpoint.as_view(), + name="release-notes", + ), + ## End Release Notes ] diff --git a/apiserver/plane/api/views/__init__.py b/apiserver/plane/api/views/__init__.py index 82eb49e44..adeb285f0 100644 --- a/apiserver/plane/api/views/__init__.py +++ b/apiserver/plane/api/views/__init__.py @@ -138,3 +138,6 @@ from .estimate import ( ProjectEstimatePointEndpoint, BulkEstimatePointEndpoint, ) + + +from .release import ReleaseNotesEndpoint diff --git a/apiserver/plane/api/views/release.py b/apiserver/plane/api/views/release.py new file mode 100644 index 000000000..de827c896 --- /dev/null +++ b/apiserver/plane/api/views/release.py @@ -0,0 +1,21 @@ +# Third party imports +from rest_framework.response import Response +from rest_framework import status +from sentry_sdk import capture_exception + +# Module imports +from .base import BaseAPIView +from plane.utils.integrations.github import get_release_notes + + +class ReleaseNotesEndpoint(BaseAPIView): + def get(self, request): + try: + release_notes = get_release_notes() + return Response(release_notes, status=status.HTTP_200_OK) + except Exception as e: + capture_exception(e) + return Response( + {"error": "Something went wrong please try again later"}, + status=status.HTTP_400_BAD_REQUEST, + ) diff --git a/apiserver/plane/settings/local.py b/apiserver/plane/settings/local.py index c3bf65588..e03a0b822 100644 --- a/apiserver/plane/settings/local.py +++ b/apiserver/plane/settings/local.py @@ -83,3 +83,6 @@ LOGGER_BASE_URL = os.environ.get("LOGGER_BASE_URL", False) CELERY_RESULT_BACKEND = os.environ.get("REDIS_URL") CELERY_BROKER_URL = os.environ.get("REDIS_URL") + + +GITHUB_ACCESS_TOKEN = os.environ.get("GITHUB_ACCESS_TOKEN", False) \ No newline at end of file diff --git a/apiserver/plane/settings/production.py b/apiserver/plane/settings/production.py index d8f2a8bb7..6598a7c29 100644 --- a/apiserver/plane/settings/production.py +++ b/apiserver/plane/settings/production.py @@ -240,7 +240,9 @@ SLACK_BOT_TOKEN = os.environ.get("SLACK_BOT_TOKEN", False) LOGGER_BASE_URL = os.environ.get("LOGGER_BASE_URL", False) redis_url = os.environ.get("REDIS_URL") -broker_url = f"{redis_url}?ssl_cert_reqs={ssl.CERT_NONE.name}&ssl_ca_certs={certifi.where()}" +broker_url = ( + f"{redis_url}?ssl_cert_reqs={ssl.CERT_NONE.name}&ssl_ca_certs={certifi.where()}" +) if DOCKERIZED: CELERY_BROKER_URL = REDIS_URL @@ -248,3 +250,5 @@ if DOCKERIZED: else: CELERY_RESULT_BACKEND = broker_url CELERY_BROKER_URL = broker_url + +GITHUB_ACCESS_TOKEN = os.environ.get("GITHUB_ACCESS_TOKEN", False) diff --git a/apiserver/plane/settings/staging.py b/apiserver/plane/settings/staging.py index 384116ba3..b43327c09 100644 --- a/apiserver/plane/settings/staging.py +++ b/apiserver/plane/settings/staging.py @@ -203,4 +203,6 @@ redis_url = os.environ.get("REDIS_URL") broker_url = f"{redis_url}?ssl_cert_reqs={ssl.CERT_NONE.name}&ssl_ca_certs={certifi.where()}" CELERY_RESULT_BACKEND = broker_url -CELERY_BROKER_URL = broker_url \ No newline at end of file +CELERY_BROKER_URL = broker_url + +GITHUB_ACCESS_TOKEN = os.environ.get("GITHUB_ACCESS_TOKEN", False) diff --git a/apiserver/plane/utils/integrations/github.py b/apiserver/plane/utils/integrations/github.py index d9185cb10..d9aecece1 100644 --- a/apiserver/plane/utils/integrations/github.py +++ b/apiserver/plane/utils/integrations/github.py @@ -5,6 +5,7 @@ from urllib.parse import urlparse, parse_qs from datetime import datetime, timedelta from cryptography.hazmat.primitives.serialization import load_pem_private_key from cryptography.hazmat.backends import default_backend +from django.conf import settings def get_jwt_token(): @@ -128,3 +129,24 @@ def get_github_repo_details(access_tokens_url, owner, repo): ).json() return open_issues, total_labels, collaborators + + +def get_release_notes(): + token = settings.GITHUB_ACCESS_TOKEN + + if token: + headers = { + "Authorization": "Bearer " + str(token), + "Accept": "application/vnd.github.v3+json", + } + else: + headers = { + "Accept": "application/vnd.github.v3+json", + } + url = "https://api.github.com/repos/makeplane/plane/releases?per_page=5&page=1" + response = requests.get(url, headers=headers) + + if response.status_code != 200: + return {"error": "Unable to render information from Github Repository"} + + return response.json() From 5b6caadd6fa1f320bdd60c0197e4b50e6220353e Mon Sep 17 00:00:00 2001 From: pablohashescobar <118773738+pablohashescobar@users.noreply.github.com> Date: Thu, 20 Apr 2023 18:14:05 +0530 Subject: [PATCH 28/43] chore: state delete validations endpoint (#880) --- apiserver/plane/api/urls.py | 6 +++++ apiserver/plane/api/views/__init__.py | 2 +- apiserver/plane/api/views/state.py | 39 ++++++++++++++++++++++++--- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/apiserver/plane/api/urls.py b/apiserver/plane/api/urls.py index 029358c98..f064d370a 100644 --- a/apiserver/plane/api/urls.py +++ b/apiserver/plane/api/urls.py @@ -79,6 +79,7 @@ from plane.api.views import ( ## End Issues # States StateViewSet, + StateDeleteIssueCheckEndpoint, ## End States # Estimates EstimateViewSet, @@ -509,6 +510,11 @@ urlpatterns = [ ), name="project-state", ), + path( + "workspaces//projects//states//", + StateDeleteIssueCheckEndpoint.as_view(), + name="state-delete-check", + ), # End States ## # States path( diff --git a/apiserver/plane/api/views/__init__.py b/apiserver/plane/api/views/__init__.py index adeb285f0..18809cd9d 100644 --- a/apiserver/plane/api/views/__init__.py +++ b/apiserver/plane/api/views/__init__.py @@ -42,7 +42,7 @@ from .workspace import ( UserWorkspaceDashboardEndpoint, WorkspaceThemeViewSet, ) -from .state import StateViewSet +from .state import StateViewSet, StateDeleteIssueCheckEndpoint from .shortcut import ShortCutViewSet from .view import IssueViewViewSet, ViewIssuesEndpoint, IssueViewFavoriteViewSet from .cycle import ( diff --git a/apiserver/plane/api/views/state.py b/apiserver/plane/api/views/state.py index 048ac4a6a..f1e409e14 100644 --- a/apiserver/plane/api/views/state.py +++ b/apiserver/plane/api/views/state.py @@ -11,10 +11,10 @@ from sentry_sdk import capture_exception # Module imports -from . import BaseViewSet +from . import BaseViewSet, BaseAPIView from plane.api.serializers import StateSerializer from plane.api.permissions import ProjectEntityPermission -from plane.db.models import State +from plane.db.models import State, Issue class StateViewSet(BaseViewSet): @@ -53,7 +53,10 @@ class StateViewSet(BaseViewSet): ) except Exception as e: capture_exception(e) - return Response({"error": "Something went wrong please try again later"}, status=status.HTTP_400_BAD_REQUEST) + return Response( + {"error": "Something went wrong please try again later"}, + status=status.HTTP_400_BAD_REQUEST, + ) def list(self, request, slug, project_id): try: @@ -85,7 +88,37 @@ class StateViewSet(BaseViewSet): {"error": "Default state cannot be deleted"}, status=False ) + # Check for any issues in the state + issue_exist = Issue.objects.filter(state=pk).exists() + + if issue_exist: + return Response( + { + "error": "The state is not empty, only empty states can be deleted" + }, + status=status.HTTP_400_BAD_REQUEST, + ) + state.delete() return Response(status=status.HTTP_204_NO_CONTENT) except State.DoesNotExist: return Response({"error": "State does not exists"}, status=status.HTTP_404) + + +class StateDeleteIssueCheckEndpoint(BaseAPIView): + permission_classes = [ + ProjectEntityPermission, + ] + + def get(self, request, slug, project_id, pk): + try: + issue_count = Issue.objects.filter( + state=pk, workspace__slug=slug, project_id=project_id + ).count() + return Response({"issue_count": issue_count}, status=status.HTTP_200_OK) + except Exception as e: + capture_exception(e) + return Response( + {"error": "Something went wrong please try again later"}, + status=status.HTTP_400_BAD_REQUEST, + ) From e68a5382f9224791961f3304475eb130b604ffdd Mon Sep 17 00:00:00 2001 From: pablohashescobar <118773738+pablohashescobar@users.noreply.github.com> Date: Thu, 20 Apr 2023 18:14:24 +0530 Subject: [PATCH 29/43] feat: sub issue state distribution (#885) * feat: sub issue state distribution * dev: update the response structure to match consistency * dev: update the query for sub issue distribution to include 0 issue groups as well --- apiserver/plane/api/views/issue.py | 35 +++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/apiserver/plane/api/views/issue.py b/apiserver/plane/api/views/issue.py index 1f604d271..c0f54bcdb 100644 --- a/apiserver/plane/api/views/issue.py +++ b/apiserver/plane/api/views/issue.py @@ -1,13 +1,14 @@ # Python imports import json import random -from itertools import groupby, chain +from itertools import chain # Django imports -from django.db.models import Prefetch, OuterRef, Func, F, Q +from django.db.models import Prefetch, OuterRef, Func, F, Q, Count from django.core.serializers.json import DjangoJSONEncoder from django.utils.decorators import method_decorator from django.views.decorators.gzip import gzip_page +from django.db.models.functions import Coalesce # Third Party imports from rest_framework.response import Response @@ -46,6 +47,7 @@ from plane.db.models import ( Label, IssueLink, IssueAttachment, + State, ) from plane.bgtasks.issue_activites_task import issue_activity from plane.utils.grouper import group_results @@ -590,10 +592,33 @@ class SubIssuesEndpoint(BaseAPIView): .prefetch_related("labels") ) - serializer = IssueLiteSerializer(sub_issues, many=True) - return Response(serializer.data, status=status.HTTP_200_OK) + state_distribution = ( + State.objects.filter(workspace__slug=slug, project_id=project_id) + .annotate( + state_count=Count( + "state_issue", + filter=Q(state_issue__parent_id=issue_id), + ) + ) + .order_by("group") + .values("group", "state_count") + ) + + result = {item["group"]: item["state_count"] for item in state_distribution} + + serializer = IssueLiteSerializer( + sub_issues, + many=True, + ) + return Response( + { + "sub_issues": serializer.data, + "state_distribution": result, + }, + status=status.HTTP_200_OK, + ) except Exception as e: - capture_exception(e) + print(e) return Response( {"error": "Something went wrong please try again later"}, status=status.HTTP_400_BAD_REQUEST, From 4dda4ec6101f2b9fdac16c9c82fde7b1e92ddedd Mon Sep 17 00:00:00 2001 From: pablohashescobar <118773738+pablohashescobar@users.noreply.github.com> Date: Thu, 20 Apr 2023 18:14:38 +0530 Subject: [PATCH 30/43] chore: single endpoint to create estimate and estimate points (#897) --- apiserver/plane/api/urls.py | 5 ++++ apiserver/plane/api/views/estimate.py | 37 +++++++++++++++++++++------ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/apiserver/plane/api/urls.py b/apiserver/plane/api/urls.py index f064d370a..2c202a1c0 100644 --- a/apiserver/plane/api/urls.py +++ b/apiserver/plane/api/urls.py @@ -566,6 +566,11 @@ urlpatterns = [ ProjectEstimatePointEndpoint.as_view(), name="project-estimate-points", ), + path( + "workspaces//projects//estimates/bulk-estimate-points/", + BulkEstimatePointEndpoint.as_view(), + name="bulk-create-estimate-points", + ), path( "workspaces//projects//estimates//bulk-estimate-points/", BulkEstimatePointEndpoint.as_view(), diff --git a/apiserver/plane/api/views/estimate.py b/apiserver/plane/api/views/estimate.py index 96d0ed1a4..99374282d 100644 --- a/apiserver/plane/api/views/estimate.py +++ b/apiserver/plane/api/views/estimate.py @@ -146,11 +146,13 @@ class BulkEstimatePointEndpoint(BaseAPIView): ProjectEntityPermission, ] - def post(self, request, slug, project_id, estimate_id): + def post(self, request, slug, project_id): try: - estimate = Estimate.objects.get( - pk=estimate_id, workspace__slug=slug, project=project_id - ) + if not request.data.get("estimate", False): + return Response( + {"error": "Estimate is required"}, + status=status.HTTP_400_BAD_REQUEST, + ) estimate_points = request.data.get("estimate_points", []) @@ -160,6 +162,18 @@ class BulkEstimatePointEndpoint(BaseAPIView): status=status.HTTP_400_BAD_REQUEST, ) + estimate_serializer = EstimateSerializer(data=request.data.get("estimate")) + if not estimate_serializer.is_valid(): + return Response( + estimate_serializer.errors, status=status.HTTP_400_BAD_REQUEST + ) + try: + estimate = estimate_serializer.save(project_id=project_id) + except IntegrityError: + return Response( + {"errror": "Estimate with the name already exists"}, + status=status.HTTP_400_BAD_REQUEST, + ) estimate_points = EstimatePoint.objects.bulk_create( [ EstimatePoint( @@ -178,9 +192,17 @@ class BulkEstimatePointEndpoint(BaseAPIView): ignore_conflicts=True, ) - serializer = EstimatePointSerializer(estimate_points, many=True) + estimate_point_serializer = EstimatePointSerializer( + estimate_points, many=True + ) - return Response(serializer.data, status=status.HTTP_200_OK) + return Response( + { + "estimate": estimate_serializer.data, + "estimate_points": estimate_point_serializer.data, + }, + status=status.HTTP_200_OK, + ) except Estimate.DoesNotExist: return Response( {"error": "Estimate does not exist"}, @@ -212,7 +234,6 @@ class BulkEstimatePointEndpoint(BaseAPIView): estimate_id=estimate_id, ) - print(estimate_points) updated_estimate_points = [] for estimate_point in estimate_points: # Find the data for that estimate point @@ -238,7 +259,7 @@ class BulkEstimatePointEndpoint(BaseAPIView): {"error": "Estimate does not exist"}, status=status.HTTP_400_BAD_REQUEST ) except Exception as e: - print(e) + capture_exception(e) return Response( {"error": "Something went wrong please try again later"}, status=status.HTTP_400_BAD_REQUEST, From 73388195ef3af2beb68b4b9e7a6756663ea57171 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Thu, 20 Apr 2023 19:04:10 +0530 Subject: [PATCH 31/43] chore: page block input (#907) * fix: sidebar workspace dropdown logo * chore: changed textarea to input for page block --- .../pages/create-update-block-inline.tsx | 21 +++++++++---------- .../components/pages/single-page-block.tsx | 21 ++++++++++--------- .../app/components/rich-text-editor/index.tsx | 4 +--- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/apps/app/components/pages/create-update-block-inline.tsx b/apps/app/components/pages/create-update-block-inline.tsx index d3e3dd92e..089025a23 100644 --- a/apps/app/components/pages/create-update-block-inline.tsx +++ b/apps/app/components/pages/create-update-block-inline.tsx @@ -16,7 +16,7 @@ import aiService from "services/ai.service"; // hooks import useToast from "hooks/use-toast"; // ui -import { Loader, PrimaryButton, SecondaryButton, TextArea } from "components/ui"; +import { Input, Loader, PrimaryButton, SecondaryButton, TextArea } from "components/ui"; // types import { IPageBlock } from "types"; // fetch-keys @@ -232,23 +232,22 @@ export const CreateUpdateBlockInline: React.FC = ({ return (
    -
    +
    -