diff --git a/.deepsource.toml b/.deepsource.toml index 85de1a5e8..2b40af672 100644 --- a/.deepsource.toml +++ b/.deepsource.toml @@ -1,5 +1,11 @@ version = 1 +exclude_patterns = [ + "bin/**", + "**/node_modules/", + "**/*.min.js" +] + [[analyzers]] name = "shell" diff --git a/apiserver/plane/app/serializers/user.py b/apiserver/plane/app/serializers/user.py index e073a0a52..46ab3c4a4 100644 --- a/apiserver/plane/app/serializers/user.py +++ b/apiserver/plane/app/serializers/user.py @@ -155,6 +155,16 @@ class ChangePasswordSerializer(serializers.Serializer): """ old_password = serializers.CharField(required=True) new_password = serializers.CharField(required=True) + confirm_password = serializers.CharField(required=True) + + def validate(self, data): + if data.get("old_password") == data.get("new_password"): + raise serializers.ValidationError("New password cannot be same as old password.") + + if data.get("new_password") != data.get("confirm_password"): + raise serializers.ValidationError("confirm password should be same as the new password.") + + return data class ResetPasswordSerializer(serializers.Serializer): diff --git a/apiserver/plane/app/views/auth_extended.py b/apiserver/plane/app/views/auth_extended.py index 5abd696fe..da3130e64 100644 --- a/apiserver/plane/app/views/auth_extended.py +++ b/apiserver/plane/app/views/auth_extended.py @@ -131,21 +131,13 @@ class ChangePasswordEndpoint(BaseAPIView): user = User.objects.get(pk=request.user.id) if serializer.is_valid(): - # Check old password - if not user.object.check_password(serializer.data.get("old_password")): + if not user.check_password(serializer.data.get("old_password")): return Response( {"old_password": ["Wrong password."]}, status=status.HTTP_400_BAD_REQUEST, ) # set_password also hashes the password that the user will get - self.object.set_password(serializer.data.get("new_password")) - self.object.save() - response = { - "status": "success", - "code": status.HTTP_200_OK, - "message": "Password updated successfully", - } - - return Response(response) - + user.set_password(serializer.data.get("new_password")) + user.save() + return Response({"message": "Password updated successfully"}, status=status.HTTP_200_OK) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) diff --git a/packages/editor/document-editor/package.json b/packages/editor/document-editor/package.json index 5f82a35f7..0016d5261 100644 --- a/packages/editor/document-editor/package.json +++ b/packages/editor/document-editor/package.json @@ -1,6 +1,6 @@ { "name": "@plane/document-editor", - "version": "0.0.1", + "version": "0.1.0", "description": "Package that powers Plane's Pages Editor", "main": "./dist/index.mjs", "module": "./dist/index.mjs", diff --git a/packages/editor/document-editor/src/ui/components/heading-component.tsx b/packages/editor/document-editor/src/ui/components/heading-component.tsx index 629d3b427..2a71beebf 100644 --- a/packages/editor/document-editor/src/ui/components/heading-component.tsx +++ b/packages/editor/document-editor/src/ui/components/heading-component.tsx @@ -8,6 +8,7 @@ export const HeadingComp = ({

{heading}

@@ -23,6 +24,7 @@ export const SubheadingComp = ({

{subHeading}

diff --git a/packages/editor/document-editor/src/ui/components/page-renderer.tsx b/packages/editor/document-editor/src/ui/components/page-renderer.tsx index 194152dd3..cff043171 100644 --- a/packages/editor/document-editor/src/ui/components/page-renderer.tsx +++ b/packages/editor/document-editor/src/ui/components/page-renderer.tsx @@ -18,7 +18,7 @@ export const PageRenderer = (props: IPageRenderer) => { } = props; return ( -
+

{documentDetails.title}

diff --git a/packages/editor/document-editor/src/ui/components/summary-popover.tsx b/packages/editor/document-editor/src/ui/components/summary-popover.tsx index 7c85ed945..46b99d048 100644 --- a/packages/editor/document-editor/src/ui/components/summary-popover.tsx +++ b/packages/editor/document-editor/src/ui/components/summary-popover.tsx @@ -44,7 +44,7 @@ export const SummaryPopover: React.FC = (props) => { {!sidePeekVisible && (
-
-
+
+
-
-
+
+
= ({ aria-hidden="true" height={height} width={width} - className={`mr-2 animate-spin fill-blue-600 text-custom-text-200 ${className}`} + className={`animate-spin fill-blue-600 text-custom-text-200 ${className}`} viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg" diff --git a/space/components/issues/board-views/kanban/block.tsx b/space/components/issues/board-views/kanban/block.tsx index b2effc4ad..e44f1dba0 100644 --- a/space/components/issues/board-views/kanban/block.tsx +++ b/space/components/issues/board-views/kanban/block.tsx @@ -43,7 +43,11 @@ export const IssueListBlock = observer(({ issue }: { issue: IIssue }) => {
{/* name */} -
+
{issue.name}
diff --git a/space/helpers/common.helper.ts b/space/helpers/common.helper.ts index 758d7c370..7c3d14157 100644 --- a/space/helpers/common.helper.ts +++ b/space/helpers/common.helper.ts @@ -1 +1,6 @@ -export const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL ? process.env.NEXT_PUBLIC_API_BASE_URL : ""; +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL ?? ""; + +export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs)); \ No newline at end of file diff --git a/space/package.json b/space/package.json index 5f3f60dc9..ddab959ab 100644 --- a/space/package.json +++ b/space/package.json @@ -17,9 +17,10 @@ "@emotion/styled": "^11.11.0", "@headlessui/react": "^1.7.13", "@mui/material": "^5.14.1", - "@plane/ui": "*", "@plane/lite-text-editor": "*", "@plane/rich-text-editor": "*", + "@plane/ui": "*", + "@plane/document-editor": "*", "axios": "^1.3.4", "clsx": "^2.0.0", "js-cookie": "^3.0.1", @@ -35,7 +36,7 @@ "react-dom": "^18.2.0", "react-hook-form": "^7.38.0", "swr": "^2.2.2", - "tailwind-merge": "^1.14.0", + "tailwind-merge": "^2.0.0", "typescript": "4.9.5", "uuid": "^9.0.0" }, @@ -43,7 +44,7 @@ "@types/js-cookie": "^3.0.3", "@types/node": "18.14.1", "@types/nprogress": "^0.2.0", - "@types/react": "18.0.28", + "@types/react": "18.2.35", "@types/react-dom": "18.0.11", "@types/uuid": "^9.0.1", "@typescript-eslint/eslint-plugin": "^5.48.2", diff --git a/web/components/account/delete-account-modal.tsx b/web/components/account/deactivate-account-modal.tsx similarity index 67% rename from web/components/account/delete-account-modal.tsx rename to web/components/account/deactivate-account-modal.tsx index 844c3837e..22a5d5b89 100644 --- a/web/components/account/delete-account-modal.tsx +++ b/web/components/account/deactivate-account-modal.tsx @@ -1,18 +1,17 @@ -// react import React, { useState } from "react"; -// next import { useRouter } from "next/router"; -// components +import { mutate } from "swr"; +import { useTheme } from "next-themes"; +import { Dialog, Transition } from "@headlessui/react"; +import { AlertTriangle } from "lucide-react"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +// ui import { Button } from "@plane/ui"; // hooks import useToast from "hooks/use-toast"; // services import { AuthService } from "services/auth.service"; -// headless ui -import { Dialog, Transition } from "@headlessui/react"; -// icons -import { AlertTriangle } from "lucide-react"; -import { UserService } from "services/user.service"; type Props = { isOpen: boolean; @@ -20,19 +19,40 @@ type Props = { }; const authService = new AuthService(); -const userService = new UserService(); -const DeleteAccountModal: React.FC = (props) => { +export const DeactivateAccountModal: React.FC = (props) => { const { isOpen, onClose } = props; - const [isDeleteLoading, setIsDeleteLoading] = useState(false); + + // states + const [switchingAccount, setSwitchingAccount] = useState(false); + const [isDeactivating, setIsDeactivating] = useState(false); + + const { + user: { deactivateAccount }, + } = useMobxStore(); + const router = useRouter(); + + const { setTheme } = useTheme(); + const { setToastAlert } = useToast(); - const handleSignOut = async () => { + const handleClose = () => { + setSwitchingAccount(false); + setIsDeactivating(false); + onClose(); + }; + + const handleSwitchAccount = async () => { + setSwitchingAccount(true); + await authService .signOut() .then(() => { + mutate("CURRENT_USER_DETAILS", null); + setTheme("system"); router.push("/"); + handleClose(); }) .catch(() => setToastAlert({ @@ -40,33 +60,31 @@ const DeleteAccountModal: React.FC = (props) => { title: "Error!", message: "Failed to sign out. Please try again.", }) - ); + ) + .finally(() => setSwitchingAccount(false)); }; const handleDeleteAccount = async () => { - setIsDeleteLoading(true); - await userService - .deleteAccount() + setIsDeactivating(true); + + await deactivateAccount() .then(() => { setToastAlert({ type: "success", title: "Success!", message: "Account deleted successfully.", }); + handleClose(); router.push("/"); }) .catch((err) => setToastAlert({ type: "error", title: "Error!", - message: err?.data?.error, + message: err?.error, }) - ); - setIsDeleteLoading(false); - }; - - const handleClose = () => { - onClose(); + ) + .finally(() => setIsDeactivating(false)); }; return ( @@ -99,32 +117,29 @@ const DeleteAccountModal: React.FC = (props) => {
-
+
- Not the right workspace? + Deactivate account?
    -
  • Delete this account if you have another and won’t use this account.
  • -
  • Switch to another account if you’d like to come back to this account another time.
  • +
  • Deactivate this account if you have another and won{"'"}t use this account.
  • +
  • Switch to another account if you{"'"}d like to come back to this account another time.
-
- - Switch account - - +
+ +
@@ -134,5 +149,3 @@ const DeleteAccountModal: React.FC = (props) => { ); }; - -export default DeleteAccountModal; diff --git a/web/components/account/email-code-form.tsx b/web/components/account/email-code-form.tsx index 560306c3b..ffaca2e6b 100644 --- a/web/components/account/email-code-form.tsx +++ b/web/components/account/email-code-form.tsx @@ -1,17 +1,16 @@ import React, { useEffect, useState } from "react"; import { Controller, useForm } from "react-hook-form"; +import { XCircle } from "lucide-react"; // ui import { Button, Input } from "@plane/ui"; +// components +import { AuthType } from "components/page-views"; // services import { AuthService } from "services/auth.service"; // hooks import useToast from "hooks/use-toast"; import useTimer from "hooks/use-timer"; -// icons -import { XCircle } from "lucide-react"; -import { useTheme } from "next-themes"; -// types type EmailCodeFormValues = { email: string; key?: string; @@ -20,7 +19,14 @@ type EmailCodeFormValues = { const authService = new AuthService(); -export const EmailCodeForm = ({ handleSignIn }: any) => { +type Props = { + handleSignIn: any; + authType: AuthType; +}; + +export const EmailCodeForm: React.FC = (Props) => { + const { handleSignIn, authType } = Props; + // states const [codeSent, setCodeSent] = useState(false); const [codeResent, setCodeResent] = useState(false); const [isCodeResending, setIsCodeResending] = useState(false); @@ -37,7 +43,6 @@ export const EmailCodeForm = ({ handleSignIn }: any) => { setError, setValue, getValues, - watch, formState: { errors, isSubmitting, isValid, isDirty }, } = useForm({ defaultValues: { @@ -49,14 +54,13 @@ export const EmailCodeForm = ({ handleSignIn }: any) => { reValidateMode: "onChange", }); - const isResendDisabled = resendCodeTimer > 0 || isCodeResending || isSubmitting || errorResendingCode; + const isResendDisabled = resendCodeTimer > 0 || isCodeResending || isSubmitting; const onSubmit = async ({ email }: EmailCodeFormValues) => { setErrorResendingCode(false); await authService .emailCode({ email }) .then((res) => { - console.log(res); setSentEmail(email); setValue("key", res.key); setCodeSent(true); @@ -139,12 +143,20 @@ export const EmailCodeForm = ({ handleSignIn }: any) => { ) : ( <>

- Let’s get you prepped! + {authType === "sign-in" ? "Get on your flight deck!" : "Let’s get you prepped!"}

-

- This whole thing will take less than two minutes. -

-

Promise!

+ {authType == "sign-up" ? ( +
+

+ This whole thing will take less than two minutes. +

+

Promise!

+
+ ) : ( +

+ Sign in with the email you used to sign up for Plane +

+ )} )} @@ -216,11 +228,39 @@ export const EmailCodeForm = ({ handleSignIn }: any) => { onChange={onChange} ref={ref} hasError={Boolean(errors.token)} - placeholder="get-set-fly" + placeholder="gets-sets-flys" className="border-onboarding-border-100 h-[46px] w-full" /> )} /> + {resendCodeTimer <= 0 && !isResendDisabled && ( + + )} +
+
+ {resendCodeTimer > 0 ? ( + Request new code in {resendCodeTimer}s + ) : isCodeResending ? ( + "Sending new code..." + ) : null}
)} @@ -238,8 +278,8 @@ export const EmailCodeForm = ({ handleSignIn }: any) => { > {isLoading ? "Signing in..." : "Next step"} -
-

+

+

When you click the button above, you agree with our{" "} ; clientId: string; + authType: AuthType; } export const GithubLoginButton: FC = (props) => { - const { handleSignIn, clientId } = props; + const { handleSignIn, clientId, authType } = props; // states const [loginCallBackURL, setLoginCallBackURL] = useState(undefined); const [gitCode, setGitCode] = useState(null); @@ -24,7 +26,7 @@ export const GithubLoginButton: FC = (props) => { query: { code }, } = useRouter(); // theme - const { theme } = useTheme(); + const { resolvedTheme } = useTheme(); useEffect(() => { if (code && !gitCode) { @@ -37,22 +39,23 @@ export const GithubLoginButton: FC = (props) => { const origin = typeof window !== "undefined" && window.location.origin ? window.location.origin : ""; setLoginCallBackURL(`${origin}/` as any); }, []); - return (

diff --git a/web/components/account/index.ts b/web/components/account/index.ts index 36a204c62..4633d91b3 100644 --- a/web/components/account/index.ts +++ b/web/components/account/index.ts @@ -1,3 +1,4 @@ +export * from "./deactivate-account-modal"; export * from "./email-code-form"; export * from "./email-password-form"; export * from "./email-forgot-password-form"; diff --git a/web/components/account/sidebar.tsx b/web/components/account/sidebar.tsx index 284719176..3604df485 100644 --- a/web/components/account/sidebar.tsx +++ b/web/components/account/sidebar.tsx @@ -1,12 +1,7 @@ import React, { useEffect } from "react"; -import { Avatar, DiceIcon, PhotoFilterIcon } from "@plane/ui"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; -// react-hook-form +import { useTheme } from "next-themes"; +import Image from "next/image"; import { Control, Controller, UseFormSetValue, UseFormWatch } from "react-hook-form"; -// types -import { IWorkspace } from "types"; -// icons import { BarChart2, Briefcase, @@ -19,7 +14,16 @@ import { PenSquare, Search, Settings, + Bell, } from "lucide-react"; +import { Avatar, DiceIcon, PhotoFilterIcon } from "@plane/ui"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; + +// types +import { IWorkspace } from "types"; +// assets +import projectEmoji from "public/emoji/project-emoji.svg"; const workspaceLinks = [ { @@ -39,7 +43,7 @@ const workspaceLinks = [ name: "All Issues", }, { - Icon: CheckCircle, + Icon: Bell, name: "Notifications", }, ]; @@ -89,22 +93,23 @@ const DummySidebar: React.FC = (props) => { const { workspace: workspaceStore, user: userStore } = useMobxStore(); const workspace = workspaceStore.workspaces ? workspaceStore.workspaces[0] : null; + const { resolvedTheme } = useTheme(); + const handleZoomWorkspace = (value: string) => { // console.log(lastWorkspaceName,value); if (lastWorkspaceName === value) return; lastWorkspaceName = value; if (timer > 0) { timer += 2; - timer = Math.min(timer, 4); + timer = Math.min(timer, 2); } else { timer = 2; - timer = Math.min(timer, 4); + timer = Math.min(timer, 2); const interval = setInterval(() => { if (timer < 0) { setValue!("name", lastWorkspaceName); clearInterval(interval); } - console.log("timer", timer); timer--; }, 1000); } @@ -112,7 +117,7 @@ const DummySidebar: React.FC = (props) => { useEffect(() => { if (watch) { - watch(); + watch("name"); } }); @@ -126,22 +131,34 @@ const DummySidebar: React.FC = (props) => { render={({ field: { value } }) => { if (value.length > 0) { handleZoomWorkspace(value); + } else { + lastWorkspaceName = ""; } return timer > 0 ? ( -
-
-
- 0 ? value[0].toLocaleUpperCase() : "N"} - src={""} - size={30} - shape="square" - fallbackBackgroundColor="black" - className="!text-base" - /> -
+
+
+
+
+ 0 ? value[0].toLocaleUpperCase() : "N"} + src={""} + size={30} + shape="square" + fallbackBackgroundColor="black" + className="!text-base" + /> +
- {value} + {value} +
) : ( @@ -206,7 +223,7 @@ const DummySidebar: React.FC = (props) => {
@@ -217,7 +234,7 @@ const DummySidebar: React.FC = (props) => {
@@ -244,11 +261,15 @@ const DummySidebar: React.FC = (props) => {
{" "}
- Plane web +
+ Plane Logo + Plane +
+
{projectLinks.map((link) => ( -
+
(
-
+
= 2 ? "bg-custom-primary-100" : "bg-onboarding-background-100"}`} />
= 2 ? "bg-custom-primary-100 h-4 w-4" : " h-3 w-3 bg-onboarding-background-100" + step >= 2 ? "bg-custom-primary-100 h-3 w-3" : " h-2 w-2 bg-onboarding-background-100" }`} />
= 3 ? "bg-custom-primary-100" : "bg-onboarding-background-100"}`} />
= 3 ? "bg-custom-primary-100 h-4 w-4" : "h-3 w-3 bg-onboarding-background-100" + step >= 3 ? "bg-custom-primary-100 h-3 w-3" : "h-2 w-2 bg-onboarding-background-100" }`} />
diff --git a/web/components/api-token/ApiTokenForm/ApiTokenDescription.tsx b/web/components/api-token/ApiTokenForm/ApiTokenDescription.tsx deleted file mode 100644 index d17e4662e..000000000 --- a/web/components/api-token/ApiTokenForm/ApiTokenDescription.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { TextArea } from "@plane/ui"; -import { Control, Controller, FieldErrors } from "react-hook-form"; -import { IApiToken } from "types/api_token"; -import { IApiFormFields } from "./types"; -import { Dispatch, SetStateAction } from "react"; - -interface IApiTokenDescription { - generatedToken: IApiToken | null | undefined; - control: Control; - focusDescription: boolean; - setFocusTitle: Dispatch>; - setFocusDescription: Dispatch>; -} - -export const ApiTokenDescription = ({ - generatedToken, - control, - focusDescription, - setFocusTitle, - setFocusDescription, -}: IApiTokenDescription) => ( - - focusDescription ? ( -