From f0faf028c283415cf721aad7309d52a99cb4b99c Mon Sep 17 00:00:00 2001 From: guru_sainath Date: Wed, 8 May 2024 12:25:36 +0530 Subject: [PATCH] chore: handled authentication in space and web apps --- .../provider/credentials/magic_code.py | 2 +- space/components/accounts/onboarding-form.tsx | 2 +- .../issues/board-views/kanban/block.tsx | 2 +- .../issues/board-views/kanban/header.tsx | 2 +- .../issues/board-views/kanban/index.tsx | 2 +- .../issues/board-views/list/block.tsx | 2 +- .../issues/board-views/list/header.tsx | 2 +- .../issues/board-views/list/index.tsx | 2 +- .../issues/filters/applied-filters/root.tsx | 2 +- space/components/issues/filters/root.tsx | 2 +- space/components/issues/navbar/index.tsx | 3 +- .../issues/navbar/issue-board-view.tsx | 2 +- .../peek-overview/comment/add-comment.tsx | 4 +- .../comment/comment-detail-card.tsx | 4 +- .../comment/comment-reactions.tsx | 7 +- .../issues/peek-overview/header.tsx | 2 +- .../issues/peek-overview/issue-activity.tsx | 5 +- .../peek-overview/issue-emoji-reactions.tsx | 6 +- .../issues/peek-overview/issue-reaction.tsx | 2 +- .../peek-overview/issue-vote-reactions.tsx | 8 +- .../issues/peek-overview/layout.tsx | 2 +- space/components/views/auth.tsx | 4 +- space/components/views/project-details.tsx | 3 +- space/helpers/authentication.helper.tsx | 291 ++++++++++++++++++ space/hooks/store/index.ts | 2 + space/hooks/store/user-mobx-provider.ts | 10 + space/hooks/use-editor-suggestions.tsx | 2 +- space/lib/mobx/store-provider.tsx | 28 -- space/lib/wrappers/auth-wrapper.tsx | 86 ++++++ space/lib/wrappers/index.ts | 2 + .../wrappers/instance-wrapper.tsx} | 12 +- .../[workspace_slug]/[project_slug]/index.tsx | 28 +- space/pages/_app.tsx | 26 +- space/pages/accounts/forgot-password.tsx | 152 ++++----- space/pages/accounts/reset-password.tsx | 193 ++++++------ space/pages/index.tsx | 11 +- space/pages/onboarding/index.tsx | 127 ++++---- space/pages/project-not-published/index.tsx | 49 +-- space/services/api.service.ts | 2 +- space/services/authentication.service.ts | 4 +- space/services/user.service.ts | 4 +- .../{sign-in-root.tsx => auth-root.tsx} | 63 ++-- web/components/account/auth-forms/index.ts | 3 +- .../account/auth-forms/sign-up-root.tsx | 133 -------- web/helpers/authentication.helper.ts | 149 --------- web/helpers/authentication.helper.tsx | 290 +++++++++++++++++ web/lib/wrappers/instance-wrapper.tsx | 7 +- web/pages/accounts/forgot-password.tsx | 4 +- web/pages/accounts/reset-password.tsx | 2 +- web/pages/accounts/set-password.tsx | 2 +- web/pages/accounts/sign-in.tsx | 8 +- web/pages/index.tsx | 8 +- 52 files changed, 1089 insertions(+), 681 deletions(-) create mode 100644 space/helpers/authentication.helper.tsx create mode 100644 space/hooks/store/user-mobx-provider.ts delete mode 100644 space/lib/mobx/store-provider.tsx create mode 100644 space/lib/wrappers/auth-wrapper.tsx create mode 100644 space/lib/wrappers/index.ts rename space/{layouts/instance-layout.tsx => lib/wrappers/instance-wrapper.tsx} (73%) rename web/components/account/auth-forms/{sign-in-root.tsx => auth-root.tsx} (66%) delete mode 100644 web/components/account/auth-forms/sign-up-root.tsx delete mode 100644 web/helpers/authentication.helper.ts create mode 100644 web/helpers/authentication.helper.tsx diff --git a/apiserver/plane/authentication/provider/credentials/magic_code.py b/apiserver/plane/authentication/provider/credentials/magic_code.py index 6686b5634..71451ef0d 100644 --- a/apiserver/plane/authentication/provider/credentials/magic_code.py +++ b/apiserver/plane/authentication/provider/credentials/magic_code.py @@ -120,7 +120,7 @@ class MagicCodeProvider(CredentialAdapter): "INVALID_MAGIC_CODE" ], error_message="INVALID_MAGIC_CODE", - payload={"email": str(self.key)}, + payload={"email": str(email)}, ) else: raise AuthenticationException( diff --git a/space/components/accounts/onboarding-form.tsx b/space/components/accounts/onboarding-form.tsx index d1afe35c1..7a719d938 100644 --- a/space/components/accounts/onboarding-form.tsx +++ b/space/components/accounts/onboarding-form.tsx @@ -8,7 +8,7 @@ import { Button, Input, Spinner, TOAST_TYPE, setToast } from "@plane/ui"; // components import { UserImageUploadModal } from "@/components/accounts"; // hooks -import { useMobxStore } from "@/lib/mobx/store-provider"; +import { useMobxStore } from "@/hooks/store"; // services import fileService from "@/services/file.service"; diff --git a/space/components/issues/board-views/kanban/block.tsx b/space/components/issues/board-views/kanban/block.tsx index c5b12a9b6..6c2aa5279 100644 --- a/space/components/issues/board-views/kanban/block.tsx +++ b/space/components/issues/board-views/kanban/block.tsx @@ -6,7 +6,7 @@ import { useRouter } from "next/router"; import { IssueBlockDueDate } from "@/components/issues/board-views/block-due-date"; import { IssueBlockPriority } from "@/components/issues/board-views/block-priority"; import { IssueBlockState } from "@/components/issues/board-views/block-state"; -import { useMobxStore } from "@/lib/mobx/store-provider"; +import { useMobxStore } from "@/hooks/store"; // components // interfaces diff --git a/space/components/issues/board-views/kanban/header.tsx b/space/components/issues/board-views/kanban/header.tsx index fc0ed274a..c08f89975 100644 --- a/space/components/issues/board-views/kanban/header.tsx +++ b/space/components/issues/board-views/kanban/header.tsx @@ -6,7 +6,7 @@ import { StateGroupIcon } from "@plane/ui"; import { issueGroupFilter } from "@/constants/data"; // ui // mobx hook -import { useMobxStore } from "@/lib/mobx/store-provider"; +import { useMobxStore } from "@/hooks/store"; import { RootStore } from "@/store/root.store"; import { IIssueState } from "types/issue"; diff --git a/space/components/issues/board-views/kanban/index.tsx b/space/components/issues/board-views/kanban/index.tsx index ccfefd874..d1a9fe709 100644 --- a/space/components/issues/board-views/kanban/index.tsx +++ b/space/components/issues/board-views/kanban/index.tsx @@ -9,7 +9,7 @@ import { IssueKanBanHeader } from "@/components/issues/board-views/kanban/header import { Icon } from "@/components/ui"; // interfaces // mobx hook -import { useMobxStore } from "@/lib/mobx/store-provider"; +import { useMobxStore } from "@/hooks/store"; import { RootStore } from "@/store/root.store"; import { IIssueState, IIssue } from "types/issue"; diff --git a/space/components/issues/board-views/list/block.tsx b/space/components/issues/board-views/list/block.tsx index b761f5163..63b589066 100644 --- a/space/components/issues/board-views/list/block.tsx +++ b/space/components/issues/board-views/list/block.tsx @@ -7,7 +7,7 @@ import { IssueBlockLabels } from "@/components/issues/board-views/block-labels"; import { IssueBlockPriority } from "@/components/issues/board-views/block-priority"; import { IssueBlockState } from "@/components/issues/board-views/block-state"; // mobx hook -import { useMobxStore } from "@/lib/mobx/store-provider"; +import { useMobxStore } from "@/hooks/store"; // interfaces import { RootStore } from "@/store/root.store"; import { IIssue } from "types/issue"; diff --git a/space/components/issues/board-views/list/header.tsx b/space/components/issues/board-views/list/header.tsx index f857158d9..6266c9cef 100644 --- a/space/components/issues/board-views/list/header.tsx +++ b/space/components/issues/board-views/list/header.tsx @@ -6,7 +6,7 @@ import { StateGroupIcon } from "@plane/ui"; // constants import { issueGroupFilter } from "@/constants/data"; // mobx hook -import { useMobxStore } from "@/lib/mobx/store-provider"; +import { useMobxStore } from "@/hooks/store"; import { RootStore } from "@/store/root.store"; import { IIssueState } from "types/issue"; diff --git a/space/components/issues/board-views/list/index.tsx b/space/components/issues/board-views/list/index.tsx index a0da35522..03ca07998 100644 --- a/space/components/issues/board-views/list/index.tsx +++ b/space/components/issues/board-views/list/index.tsx @@ -4,7 +4,7 @@ import { IssueListBlock } from "@/components/issues/board-views/list/block"; import { IssueListHeader } from "@/components/issues/board-views/list/header"; // interfaces // mobx hook -import { useMobxStore } from "@/lib/mobx/store-provider"; +import { useMobxStore } from "@/hooks/store"; // store import { RootStore } from "@/store/root.store"; import { IIssueState, IIssue } from "types/issue"; diff --git a/space/components/issues/filters/applied-filters/root.tsx b/space/components/issues/filters/applied-filters/root.tsx index 432d187cf..7362e8787 100644 --- a/space/components/issues/filters/applied-filters/root.tsx +++ b/space/components/issues/filters/applied-filters/root.tsx @@ -3,7 +3,7 @@ import { observer } from "mobx-react-lite"; import { useRouter } from "next/router"; // components // store -import { useMobxStore } from "@/lib/mobx/store-provider"; +import { useMobxStore } from "@/hooks/store"; import { IIssueFilterOptions } from "@/store/issues/types"; import { RootStore } from "@/store/root.store"; import { AppliedFiltersList } from "./filters-list"; diff --git a/space/components/issues/filters/root.tsx b/space/components/issues/filters/root.tsx index 2e9600774..e14412c32 100644 --- a/space/components/issues/filters/root.tsx +++ b/space/components/issues/filters/root.tsx @@ -2,7 +2,7 @@ import { FC, useCallback } from "react"; import { observer } from "mobx-react-lite"; import { useRouter } from "next/router"; // components -import { useMobxStore } from "@/lib/mobx/store-provider"; +import { useMobxStore } from "@/hooks/store"; import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "@/store/issues/helpers"; import { IIssueFilterOptions } from "@/store/issues/types"; import { RootStore } from "@/store/root.store"; diff --git a/space/components/issues/navbar/index.tsx b/space/components/issues/navbar/index.tsx index ccd903a80..de8633100 100644 --- a/space/components/issues/navbar/index.tsx +++ b/space/components/issues/navbar/index.tsx @@ -8,8 +8,7 @@ import { Avatar, Button } from "@plane/ui"; import { ProjectLogo } from "@/components/common"; import { IssueFiltersDropdown } from "@/components/issues/filters"; // hooks -import { useUser } from "@/hooks/store"; -import { useMobxStore } from "@/lib/mobx/store-provider"; +import { useMobxStore, useUser } from "@/hooks/store"; // store import { RootStore } from "@/store/root.store"; import { TIssueBoardKeys } from "@/types/issue"; diff --git a/space/components/issues/navbar/issue-board-view.tsx b/space/components/issues/navbar/issue-board-view.tsx index 3b81aaf68..12574bef8 100644 --- a/space/components/issues/navbar/issue-board-view.tsx +++ b/space/components/issues/navbar/issue-board-view.tsx @@ -3,7 +3,7 @@ import { useRouter } from "next/router"; // constants import { issueViews } from "@/constants/data"; // mobx -import { useMobxStore } from "@/lib/mobx/store-provider"; +import { useMobxStore } from "@/hooks/store"; import { RootStore } from "@/store/root.store"; import { TIssueBoardKeys } from "types/issue"; diff --git a/space/components/issues/peek-overview/comment/add-comment.tsx b/space/components/issues/peek-overview/comment/add-comment.tsx index 059b224c3..dadcc1747 100644 --- a/space/components/issues/peek-overview/comment/add-comment.tsx +++ b/space/components/issues/peek-overview/comment/add-comment.tsx @@ -6,10 +6,8 @@ import { useForm, Controller } from "react-hook-form"; import { EditorRefApi } from "@plane/lite-text-editor"; import { LiteTextEditor } from "@/components/editor/lite-text-editor"; // hooks -import { useUser } from "@/hooks/store"; +import { useMobxStore, useUser } from "@/hooks/store"; import useToast from "@/hooks/use-toast"; -// lib -import { useMobxStore } from "@/lib/mobx/store-provider"; // types import { Comment } from "@/types/issue"; diff --git a/space/components/issues/peek-overview/comment/comment-detail-card.tsx b/space/components/issues/peek-overview/comment/comment-detail-card.tsx index 11f11849a..e3962c5e4 100644 --- a/space/components/issues/peek-overview/comment/comment-detail-card.tsx +++ b/space/components/issues/peek-overview/comment/comment-detail-card.tsx @@ -10,9 +10,7 @@ import { CommentReactions } from "@/components/issues/peek-overview"; // helpers import { timeAgo } from "@/helpers/date-time.helper"; // hooks -import { useUser } from "@/hooks/store"; -// mobx store -import { useMobxStore } from "@/lib/mobx/store-provider"; +import { useMobxStore, useUser } from "@/hooks/store"; // store import { RootStore } from "@/store/root.store"; // types diff --git a/space/components/issues/peek-overview/comment/comment-reactions.tsx b/space/components/issues/peek-overview/comment/comment-reactions.tsx index 09e725550..ca451f7b4 100644 --- a/space/components/issues/peek-overview/comment/comment-reactions.tsx +++ b/space/components/issues/peek-overview/comment/comment-reactions.tsx @@ -1,16 +1,13 @@ import React from "react"; - -// mobx import { observer } from "mobx-react-lite"; import { useRouter } from "next/router"; -// ui import { Tooltip } from "@plane/ui"; +// ui import { ReactionSelector } from "@/components/ui"; // helpers import { groupReactions, renderEmoji } from "@/helpers/emoji.helper"; // hooks -import { useUser } from "@/hooks/store"; -import { useMobxStore } from "@/lib/mobx/store-provider"; +import { useMobxStore, useUser } from "@/hooks/store"; type Props = { commentId: string; diff --git a/space/components/issues/peek-overview/header.tsx b/space/components/issues/peek-overview/header.tsx index 413b6ac2d..7aba40305 100644 --- a/space/components/issues/peek-overview/header.tsx +++ b/space/components/issues/peek-overview/header.tsx @@ -8,7 +8,7 @@ import { Icon } from "@/components/ui"; // helpers import { copyTextToClipboard } from "@/helpers/string.helper"; // store -import { useMobxStore } from "@/lib/mobx/store-provider"; +import { useMobxStore } from "@/hooks/store"; import { IPeekMode } from "@/store/issue_details"; import { RootStore } from "@/store/root.store"; // lib diff --git a/space/components/issues/peek-overview/issue-activity.tsx b/space/components/issues/peek-overview/issue-activity.tsx index 3a5477ac6..aaa7dc688 100644 --- a/space/components/issues/peek-overview/issue-activity.tsx +++ b/space/components/issues/peek-overview/issue-activity.tsx @@ -2,13 +2,12 @@ import React from "react"; import { observer } from "mobx-react-lite"; import Link from "next/link"; import { useRouter } from "next/router"; -// lib import { Button } from "@plane/ui"; +// components import { CommentCard, AddComment } from "@/components/issues/peek-overview"; import { Icon } from "@/components/ui"; // hooks -import { useUser } from "@/hooks/store"; -import { useMobxStore } from "@/lib/mobx/store-provider"; +import { useMobxStore, useUser } from "@/hooks/store"; // types import { IIssue } from "@/types/issue"; diff --git a/space/components/issues/peek-overview/issue-emoji-reactions.tsx b/space/components/issues/peek-overview/issue-emoji-reactions.tsx index 207434100..7e568461b 100644 --- a/space/components/issues/peek-overview/issue-emoji-reactions.tsx +++ b/space/components/issues/peek-overview/issue-emoji-reactions.tsx @@ -4,12 +4,10 @@ import { useRouter } from "next/router"; // lib import { Tooltip } from "@plane/ui"; import { ReactionSelector } from "@/components/ui"; +// helpers import { groupReactions, renderEmoji } from "@/helpers/emoji.helper"; // hooks -import { useUser } from "@/hooks/store"; -import { useMobxStore } from "@/lib/mobx/store-provider"; -// helpers -// components +import { useMobxStore, useUser } from "@/hooks/store"; export const IssueEmojiReactions: React.FC = observer(() => { // router diff --git a/space/components/issues/peek-overview/issue-reaction.tsx b/space/components/issues/peek-overview/issue-reaction.tsx index eaa5bb8d5..5bc60cb34 100644 --- a/space/components/issues/peek-overview/issue-reaction.tsx +++ b/space/components/issues/peek-overview/issue-reaction.tsx @@ -1,5 +1,5 @@ import { IssueEmojiReactions, IssueVotes } from "@/components/issues/peek-overview"; -import { useMobxStore } from "@/lib/mobx/store-provider"; +import { useMobxStore } from "@/hooks/store"; export const IssueReactions: React.FC = () => { const { project: projectStore } = useMobxStore(); diff --git a/space/components/issues/peek-overview/issue-vote-reactions.tsx b/space/components/issues/peek-overview/issue-vote-reactions.tsx index b7d95a18a..64568f66c 100644 --- a/space/components/issues/peek-overview/issue-vote-reactions.tsx +++ b/space/components/issues/peek-overview/issue-vote-reactions.tsx @@ -1,15 +1,9 @@ import { useState, useEffect } from "react"; - import { observer } from "mobx-react-lite"; import { useRouter } from "next/router"; - -// mobx -// lib import { Tooltip } from "@plane/ui"; // hooks -import { useUser } from "@/hooks/store"; -import { useMobxStore } from "@/lib/mobx/store-provider"; -// ui +import { useMobxStore, useUser } from "@/hooks/store"; export const IssueVotes: React.FC = observer(() => { const [isSubmitting, setIsSubmitting] = useState(false); diff --git a/space/components/issues/peek-overview/layout.tsx b/space/components/issues/peek-overview/layout.tsx index 6662288ce..01183fb2d 100644 --- a/space/components/issues/peek-overview/layout.tsx +++ b/space/components/issues/peek-overview/layout.tsx @@ -9,7 +9,7 @@ import { Dialog, Transition } from "@headlessui/react"; // components import { FullScreenPeekView, SidePeekView } from "@/components/issues/peek-overview"; // lib -import { useMobxStore } from "@/lib/mobx/store-provider"; +import { useMobxStore } from "@/hooks/store"; export const IssuePeekOverview: React.FC = observer(() => { // states diff --git a/space/components/views/auth.tsx b/space/components/views/auth.tsx index 0594cb045..cb36a6146 100644 --- a/space/components/views/auth.tsx +++ b/space/components/views/auth.tsx @@ -20,14 +20,14 @@ export const AuthView = observer(() => { const { data: currentUser, fetchCurrentUser, isLoading } = useUser(); // fetching user information - useSWR("CURRENT_USER_DETAILS", () => fetchCurrentUser(), { + const { isLoading: isSWRLoading } = useSWR("CURRENT_USER_DETAILS", () => fetchCurrentUser(), { shouldRetryOnError: false, revalidateOnFocus: false, }); return ( <> - {isLoading ? ( + {isLoading || isSWRLoading ? (
diff --git a/space/components/views/project-details.tsx b/space/components/views/project-details.tsx index 2233edc0e..ef51a4512 100644 --- a/space/components/views/project-details.tsx +++ b/space/components/views/project-details.tsx @@ -11,8 +11,7 @@ import { IssueSpreadsheetView } from "@/components/issues/board-views/spreadshee import { IssueAppliedFilters } from "@/components/issues/filters/applied-filters/root"; import { IssuePeekOverview } from "@/components/issues/peek-overview"; // mobx store -import { useUser } from "@/hooks/store"; -import { useMobxStore } from "@/lib/mobx/store-provider"; +import { useMobxStore, useUser } from "@/hooks/store"; import { RootStore } from "@/store/root.store"; // assets import SomethingWentWrongImage from "public/something-went-wrong.svg"; diff --git a/space/helpers/authentication.helper.tsx b/space/helpers/authentication.helper.tsx new file mode 100644 index 000000000..f60f694b5 --- /dev/null +++ b/space/helpers/authentication.helper.tsx @@ -0,0 +1,291 @@ +import { ReactNode } from "react"; +import Link from "next/link"; + +export enum EPageTypes { + "INIT" = "INIT", + "PUBLIC" = "PUBLIC", + "NON_AUTHENTICATED" = "NON_AUTHENTICATED", + "ONBOARDING" = "ONBOARDING", + "AUTHENTICATED" = "AUTHENTICATED", +} + +export enum EAuthModes { + SIGN_IN = "SIGN_IN", + SIGN_UP = "SIGN_UP", +} + +export enum EAuthSteps { + EMAIL = "EMAIL", + PASSWORD = "PASSWORD", + UNIQUE_CODE = "UNIQUE_CODE", +} + +export enum EAuthenticationErrorCodes { + INSTANCE_NOT_CONFIGURED = "5000", + SIGNUP_DISABLED = "5001", + INVALID_PASSWORD = "5002", + USER_ALREADY_EXIST = "5003", + USER_DOES_NOT_EXIST = "5004", + AUTHENTICATION_FAILED_SIGN_IN = "5005", + AUTHENTICATION_FAILED_SIGN_UP = "5006", + SMTP_NOT_CONFIGURED = "5007", + INVALID_MAGIC_CODE = "5008", + EXPIRED_MAGIC_CODE = "5009", + GOOGLE_NOT_CONFIGURED = "5010", + GITHUB_NOT_CONFIGURED = "5011", + INVALID_EMAIL = "5012", + EMAIL_REQUIRED = "5013", + REQUIRED_EMAIL_PASSWORD_SIGN_IN = "5014", + INVALID_EMAIL_SIGN_IN = "5015", + INVALID_EMAIL_SIGN_UP = "5016", + INVALID_EMAIL_MAGIC_SIGN_IN = "5017", + INVALID_EMAIL_MAGIC_SIGN_UP = "5018", + GITHUB_OAUTH_PROVIDER_ERROR = "5019", + GOOGLE_OAUTH_PROVIDER_ERROR = "5020", + MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED = "5021", + MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED = "5022", + INVALID_PASSWORD_TOKEN = "5023", + EXPIRED_PASSWORD_TOKEN = "5024", + INCORRECT_OLD_PASSWORD = "5025", + INVALID_NEW_PASSWORD = "5026", + PASSWORD_ALREADY_SET = "5027", + ADMIN_ALREADY_EXIST = "5028", + REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME = "5029", + INVALID_ADMIN_EMAIL = "5030", + INVALID_ADMIN_PASSWORD = "5031", + REQUIRED_ADMIN_EMAIL_PASSWORD = "5032", + ADMIN_AUTHENTICATION_FAILED = "5034", +} + +export enum EErrorAlertType { + BANNER_ALERT = "BANNER_ALERT", + TOAST_ALERT = "TOAST_ALERT", + INLINE_FIRST_NAME = "INLINE_FIRST_NAME", + INLINE_EMAIL = "INLINE_EMAIL", + INLINE_PASSWORD = "INLINE_PASSWORD", + INLINE_EMAIL_CODE = "INLINE_EMAIL_CODE", +} + +export type TAuthErrorInfo = { + type: EErrorAlertType; + code: EAuthenticationErrorCodes; + title: string; + message: ReactNode; +}; + +const errorCodeMessages: { [key in EAuthenticationErrorCodes]: { title: string; message: ReactNode } } = { + [EAuthenticationErrorCodes.INSTANCE_NOT_CONFIGURED]: { + title: `Instance not configured`, + message: `Instance not configured. Please contact your administrator.`, + }, + [EAuthenticationErrorCodes.SIGNUP_DISABLED]: { + title: `Sign up disabled`, + message: `Sign up disabled. Please contact your administrator.`, + }, + [EAuthenticationErrorCodes.INVALID_PASSWORD]: { + title: `Invalid password`, + message: `Invalid password. Please try again.`, + }, + [EAuthenticationErrorCodes.USER_ALREADY_EXIST]: { + title: `User already exists`, + message: ( +
+ Your account is already registered.  + + Sign In + +  now. +
+ ), + }, + [EAuthenticationErrorCodes.USER_DOES_NOT_EXIST]: { + title: `User does not exist`, + message: ( +
+ No account found.  + + Create one + +  to get started. +
+ ), + }, + [EAuthenticationErrorCodes.AUTHENTICATION_FAILED_SIGN_IN]: { + title: `Authentication failed`, + message: `Authentication failed. Please try again.`, + }, + [EAuthenticationErrorCodes.AUTHENTICATION_FAILED_SIGN_UP]: { + title: `Authentication failed`, + message: `Authentication failed. Please try again.`, + }, + [EAuthenticationErrorCodes.SMTP_NOT_CONFIGURED]: { + title: `SMTP not configured`, + message: `SMTP not configured. Please contact your administrator.`, + }, + [EAuthenticationErrorCodes.INVALID_MAGIC_CODE]: { + title: `Authentication failed`, + message: `Invalid magic code. Please try again.`, + }, + [EAuthenticationErrorCodes.EXPIRED_MAGIC_CODE]: { + title: `Expired magic code`, + message: `Expired magic code. Please try again.`, + }, + [EAuthenticationErrorCodes.GOOGLE_NOT_CONFIGURED]: { + title: `Google not configured`, + message: `Google not configured. Please contact your administrator.`, + }, + [EAuthenticationErrorCodes.GITHUB_NOT_CONFIGURED]: { + title: `GitHub not configured`, + message: `GitHub not configured. Please contact your administrator.`, + }, + [EAuthenticationErrorCodes.INVALID_EMAIL]: { + title: `Invalid email`, + message: `Invalid email. Please try again.`, + }, + [EAuthenticationErrorCodes.EMAIL_REQUIRED]: { + title: `Email required`, + message: `Email required. Please try again.`, + }, + [EAuthenticationErrorCodes.REQUIRED_EMAIL_PASSWORD_SIGN_IN]: { + title: `Email and password required`, + message: `Email and password required. Please try again.`, + }, + [EAuthenticationErrorCodes.INVALID_EMAIL_SIGN_IN]: { + title: `Invalid email`, + message: `Invalid email. Please try again.`, + }, + [EAuthenticationErrorCodes.INVALID_EMAIL_SIGN_UP]: { + title: `Invalid email`, + message: `Invalid email. Please try again.`, + }, + [EAuthenticationErrorCodes.INVALID_EMAIL_MAGIC_SIGN_IN]: { + title: `Invalid email`, + message: `Invalid email. Please try again.`, + }, + [EAuthenticationErrorCodes.INVALID_EMAIL_MAGIC_SIGN_UP]: { + title: `Invalid email`, + message: `Invalid email. Please try again.`, + }, + [EAuthenticationErrorCodes.GITHUB_OAUTH_PROVIDER_ERROR]: { + title: `GitHub OAuth provider error`, + message: `GitHub OAuth provider error. Please try again.`, + }, + [EAuthenticationErrorCodes.GOOGLE_OAUTH_PROVIDER_ERROR]: { + title: `Google OAuth provider error`, + message: `Google OAuth provider error. Please try again.`, + }, + [EAuthenticationErrorCodes.MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED]: { + title: `Email and code required`, + message: `Email and code required. Please try again.`, + }, + [EAuthenticationErrorCodes.MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED]: { + title: `Email and code required`, + message: `Email and code required. Please try again.`, + }, + [EAuthenticationErrorCodes.INVALID_PASSWORD_TOKEN]: { + title: `Invalid password token`, + message: `Invalid password token. Please try again.`, + }, + [EAuthenticationErrorCodes.EXPIRED_PASSWORD_TOKEN]: { + title: `Expired password token`, + message: `Expired password token. Please try again.`, + }, + [EAuthenticationErrorCodes.INCORRECT_OLD_PASSWORD]: { + title: `Incorrect old password`, + message: `Incorrect old password. Please try again.`, + }, + [EAuthenticationErrorCodes.INVALID_NEW_PASSWORD]: { + title: `Invalid new password`, + message: `Invalid new password. Please try again.`, + }, + [EAuthenticationErrorCodes.PASSWORD_ALREADY_SET]: { + title: `Password already set`, + message: `Password already set. Please try again.`, + }, + [EAuthenticationErrorCodes.ADMIN_ALREADY_EXIST]: { + title: `Admin already exists`, + message: `Admin already exists. Please try again.`, + }, + [EAuthenticationErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME]: { + title: `Email, password and first name required`, + message: `Email, password and first name required. Please try again.`, + }, + [EAuthenticationErrorCodes.INVALID_ADMIN_EMAIL]: { + title: `Invalid email`, + message: `Invalid email. Please try again.`, + }, + [EAuthenticationErrorCodes.INVALID_ADMIN_PASSWORD]: { + title: `Invalid password`, + message: `Invalid password. Please try again.`, + }, + [EAuthenticationErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD]: { + title: `Email and password required`, + message: `Email and password required. Please try again.`, + }, + [EAuthenticationErrorCodes.ADMIN_AUTHENTICATION_FAILED]: { + title: `Authentication failed`, + message: `Authentication failed. Please try again.`, + }, +}; + +export const authErrorHandler = (errorCode: EAuthenticationErrorCodes): TAuthErrorInfo | undefined => { + const toastAlertErrorCodes = [ + EAuthenticationErrorCodes.INSTANCE_NOT_CONFIGURED, + EAuthenticationErrorCodes.SIGNUP_DISABLED, + EAuthenticationErrorCodes.INVALID_PASSWORD, + EAuthenticationErrorCodes.AUTHENTICATION_FAILED_SIGN_IN, + EAuthenticationErrorCodes.AUTHENTICATION_FAILED_SIGN_UP, + EAuthenticationErrorCodes.SMTP_NOT_CONFIGURED, + EAuthenticationErrorCodes.INVALID_MAGIC_CODE, + EAuthenticationErrorCodes.EXPIRED_MAGIC_CODE, + EAuthenticationErrorCodes.GOOGLE_NOT_CONFIGURED, + EAuthenticationErrorCodes.GITHUB_NOT_CONFIGURED, + EAuthenticationErrorCodes.INVALID_EMAIL, + EAuthenticationErrorCodes.EMAIL_REQUIRED, + EAuthenticationErrorCodes.REQUIRED_EMAIL_PASSWORD_SIGN_IN, + EAuthenticationErrorCodes.INVALID_EMAIL_SIGN_IN, + EAuthenticationErrorCodes.INVALID_EMAIL_SIGN_UP, + EAuthenticationErrorCodes.INVALID_EMAIL_MAGIC_SIGN_IN, + EAuthenticationErrorCodes.INVALID_EMAIL_MAGIC_SIGN_UP, + EAuthenticationErrorCodes.GITHUB_OAUTH_PROVIDER_ERROR, + EAuthenticationErrorCodes.GOOGLE_OAUTH_PROVIDER_ERROR, + EAuthenticationErrorCodes.MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED, + EAuthenticationErrorCodes.MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED, + EAuthenticationErrorCodes.INVALID_PASSWORD_TOKEN, + EAuthenticationErrorCodes.EXPIRED_PASSWORD_TOKEN, + EAuthenticationErrorCodes.INCORRECT_OLD_PASSWORD, + EAuthenticationErrorCodes.INVALID_NEW_PASSWORD, + EAuthenticationErrorCodes.PASSWORD_ALREADY_SET, + EAuthenticationErrorCodes.ADMIN_ALREADY_EXIST, + EAuthenticationErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME, + EAuthenticationErrorCodes.INVALID_ADMIN_EMAIL, + EAuthenticationErrorCodes.INVALID_ADMIN_PASSWORD, + EAuthenticationErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD, + EAuthenticationErrorCodes.ADMIN_AUTHENTICATION_FAILED, + ]; + const bannerAlertErrorCodes = [ + EAuthenticationErrorCodes.USER_ALREADY_EXIST, + EAuthenticationErrorCodes.USER_DOES_NOT_EXIST, + ]; + + if (toastAlertErrorCodes.includes(errorCode)) + return { + type: EErrorAlertType.TOAST_ALERT, + code: errorCode, + title: errorCodeMessages[errorCode]?.title || "Error", + message: errorCodeMessages[errorCode]?.message || "Something went wrong. Please try again.", + }; + + if (bannerAlertErrorCodes.includes(errorCode)) + return { + type: EErrorAlertType.BANNER_ALERT, + code: errorCode, + title: errorCodeMessages[errorCode]?.title || "Error", + message: errorCodeMessages[errorCode]?.message || "Something went wrong. Please try again.", + }; + + return undefined; +}; diff --git a/space/hooks/store/index.ts b/space/hooks/store/index.ts index 9a48d10f2..3b7ef07c9 100644 --- a/space/hooks/store/index.ts +++ b/space/hooks/store/index.ts @@ -1,2 +1,4 @@ +export * from "./user-mobx-provider"; + export * from "./use-instance"; export * from "./user"; diff --git a/space/hooks/store/user-mobx-provider.ts b/space/hooks/store/user-mobx-provider.ts new file mode 100644 index 000000000..4fbc5591f --- /dev/null +++ b/space/hooks/store/user-mobx-provider.ts @@ -0,0 +1,10 @@ +import { useContext } from "react"; +// store +import { StoreContext } from "@/lib/store-context"; +import { RootStore } from "@/store/root.store"; + +export const useMobxStore = (): RootStore => { + const context = useContext(StoreContext); + if (context === undefined) throw new Error("useMobxStore must be used within StoreProvider"); + return context; +}; diff --git a/space/hooks/use-editor-suggestions.tsx b/space/hooks/use-editor-suggestions.tsx index d16d310c2..937306f7b 100644 --- a/space/hooks/use-editor-suggestions.tsx +++ b/space/hooks/use-editor-suggestions.tsx @@ -1,4 +1,4 @@ -import { useMobxStore } from "@/lib/mobx/store-provider"; +import { useMobxStore } from "@/hooks/store"; import { RootStore } from "@/store/root.store"; const useEditorSuggestions = () => { diff --git a/space/lib/mobx/store-provider.tsx b/space/lib/mobx/store-provider.tsx deleted file mode 100644 index c982246f4..000000000 --- a/space/lib/mobx/store-provider.tsx +++ /dev/null @@ -1,28 +0,0 @@ -"use client"; - -import { createContext, useContext } from "react"; -// mobx store -import { RootStore } from "@/store/root.store"; - -let rootStore: RootStore = new RootStore(); - -export const MobxStoreContext = createContext(rootStore); - -const initializeStore = () => { - const singletonRootStore: RootStore = rootStore ?? new RootStore(); - if (typeof window === "undefined") return singletonRootStore; - if (!rootStore) rootStore = singletonRootStore; - return singletonRootStore; -}; - -export const MobxStoreProvider = ({ children }: any) => { - const store: RootStore = initializeStore(); - return {children}; -}; - -// hook -export const useMobxStore = () => { - const context = useContext(MobxStoreContext); - if (context === undefined) throw new Error("useMobxStore must be used within MobxStoreProvider"); - return context; -}; diff --git a/space/lib/wrappers/auth-wrapper.tsx b/space/lib/wrappers/auth-wrapper.tsx new file mode 100644 index 000000000..1dad8f337 --- /dev/null +++ b/space/lib/wrappers/auth-wrapper.tsx @@ -0,0 +1,86 @@ +import { FC, ReactNode } from "react"; +import { observer } from "mobx-react-lite"; +import { useRouter } from "next/router"; +import useSWR from "swr"; +import { Spinner } from "@plane/ui"; +// helpers +import { EPageTypes } from "@/helpers/authentication.helper"; +// hooks +import { useUser, useUserProfile } from "@/hooks/store"; + +type TAuthWrapper = { + children: ReactNode; + pageType?: EPageTypes; +}; + +export const AuthWrapper: FC = observer((props) => { + const router = useRouter(); + const { children, pageType = EPageTypes.AUTHENTICATED } = props; + // hooks + const { isLoading, data: currentUser, fetchCurrentUser } = useUser(); + const { data: currentUserProfile } = useUserProfile(); + + console; + + const { isLoading: isSWRLoading } = useSWR("INSTANCE_INFORMATION", () => fetchCurrentUser(), { + revalidateOnFocus: false, + }); + + if (isSWRLoading || isLoading) + return ( +
+ +
+ ); + + if (pageType === EPageTypes.PUBLIC) return <>{children}; + + if (pageType === EPageTypes.INIT) { + if (!currentUser?.id) return <>{children}; + else { + if (currentUserProfile?.id && currentUserProfile?.onboarding_step?.profile_complete) return <>{children}; + else { + router.push(`/onboarding`); + return <>; + } + } + } + + if (pageType === EPageTypes.NON_AUTHENTICATED) { + if (!currentUser?.id) return <>{children}; + else { + if (currentUserProfile?.id && currentUserProfile?.onboarding_step?.profile_complete) { + router.push(`/`); + return <>; + } else { + router.push(`/onboarding`); + return <>; + } + } + } + + if (pageType === EPageTypes.ONBOARDING) { + if (!currentUser?.id) { + router.push(`/`); + return <>; + } else { + if (currentUserProfile?.id && currentUserProfile?.onboarding_step?.profile_complete) { + router.push(`/`); + return <>; + } else return <>{children}; + } + } + + if (pageType === EPageTypes.AUTHENTICATED) { + if (!currentUser?.id) return <>{children}; + else { + if (currentUserProfile?.id && currentUserProfile?.onboarding_step?.profile_complete) return <>{children}; + else { + router.push(`/onboarding`); + return <>; + } + } + } + + return <>{children}; +}); diff --git a/space/lib/wrappers/index.ts b/space/lib/wrappers/index.ts new file mode 100644 index 000000000..51fab70a6 --- /dev/null +++ b/space/lib/wrappers/index.ts @@ -0,0 +1,2 @@ +export * from "./instance-wrapper"; +export * from "./auth-wrapper"; diff --git a/space/layouts/instance-layout.tsx b/space/lib/wrappers/instance-wrapper.tsx similarity index 73% rename from space/layouts/instance-layout.tsx rename to space/lib/wrappers/instance-wrapper.tsx index 65e56d50f..05390fad8 100644 --- a/space/layouts/instance-layout.tsx +++ b/space/lib/wrappers/instance-wrapper.tsx @@ -8,20 +8,20 @@ import { InstanceNotReady } from "@/components/instance"; // hooks import { useInstance } from "@/hooks/store"; -type TInstanceLayout = { +type TInstanceWrapper = { children: ReactNode; }; -const InstanceLayout: FC = observer((props) => { +export const InstanceWrapper: FC = observer((props) => { const { children } = props; - // store + // hooks const { isLoading, instance, fetchInstanceInfo } = useInstance(); - useSWR("INSTANCE_INFORMATION", () => fetchInstanceInfo(), { + const { isLoading: isSWRLoading } = useSWR("INSTANCE_INFORMATION", () => fetchInstanceInfo(), { revalidateOnFocus: false, }); - if (isLoading) + if (isSWRLoading || isLoading) return (
@@ -32,5 +32,3 @@ const InstanceLayout: FC = observer((props) => { return <>{children}; }); - -export default InstanceLayout; diff --git a/space/pages/[workspace_slug]/[project_slug]/index.tsx b/space/pages/[workspace_slug]/[project_slug]/index.tsx index 9e8a6018d..aaec7672e 100644 --- a/space/pages/[workspace_slug]/[project_slug]/index.tsx +++ b/space/pages/[workspace_slug]/[project_slug]/index.tsx @@ -1,14 +1,16 @@ import Head from "next/head"; import { useRouter } from "next/router"; - import useSWR from "swr"; - -/// layouts // components import { ProjectDetailsView } from "@/components/views"; -// lib -import { useMobxStore } from "@/lib/mobx/store-provider"; -import ProjectLayout from "layouts/project-layout"; +// helpers +import { EPageTypes } from "@/helpers/authentication.helper"; +// hooks +import { useMobxStore } from "@/hooks/store"; +// layouts +import ProjectLayout from "@/layouts/project-layout"; +// wrappers +import { AuthWrapper } from "@/lib/wrappers"; const WorkspaceProjectPage = (props: any) => { const SITE_TITLE = props?.project_settings?.project_details?.name || "Plane | Deploy"; @@ -31,12 +33,14 @@ const WorkspaceProjectPage = (props: any) => { }); return ( - - - {SITE_TITLE} - - - + + + + {SITE_TITLE} + + + + ); }; diff --git a/space/pages/_app.tsx b/space/pages/_app.tsx index ce206f49b..16f6e6efd 100644 --- a/space/pages/_app.tsx +++ b/space/pages/_app.tsx @@ -7,15 +7,15 @@ import "@/styles/globals.css"; import { SITE_NAME, SITE_DESCRIPTION, SITE_URL, TWITTER_USER_NAME, SITE_KEYWORDS, SITE_TITLE } from "@/constants/seo"; import { ToastContextProvider } from "@/contexts/toast.context"; // mobx store provider -import InstanceLayout from "@/layouts/instance-layout"; -import { MobxStoreProvider } from "@/lib/mobx/store-provider"; -// constants +import { StoreProvider } from "@/lib/store-context"; +// wrappers +import { InstanceWrapper } from "@/lib/wrappers"; const prefix = parseInt(process.env.NEXT_PUBLIC_DEPLOY_WITH_NGINX || "0") === 0 ? "/" : "/spaces/"; function MyApp({ Component, pageProps }: AppProps) { return ( - + <> {SITE_TITLE} @@ -31,14 +31,16 @@ function MyApp({ Component, pageProps }: AppProps) { - - - - - - - - + + + + + + + + + + ); } diff --git a/space/pages/accounts/forgot-password.tsx b/space/pages/accounts/forgot-password.tsx index 0538df24a..494eae9d3 100644 --- a/space/pages/accounts/forgot-password.tsx +++ b/space/pages/accounts/forgot-password.tsx @@ -9,11 +9,13 @@ import { CircleCheck } from "lucide-react"; // ui import { Button, Input, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui"; // helpers +import { EPageTypes } from "@/helpers/authentication.helper"; import { cn } from "@/helpers/common.helper"; import { checkEmailValidity } from "@/helpers/string.helper"; // hooks -// import useAuthRedirection from "@/hooks/use-auth-redirection"; import useTimer from "@/hooks/use-timer"; +// wrappers +import { AuthWrapper } from "@/lib/wrappers"; // services import { AuthService } from "@/services/authentication.service"; // images @@ -77,85 +79,87 @@ const ForgotPasswordPage: NextPage = () => { }; return ( -
-
- Plane background pattern -
-
-
-
- Plane Logo - Plane -
+ +
+
+ Plane background pattern
-
-
-
-
-

- Reset your password -

-

- Enter your user account{"'"}s verified email address and we will send you a password reset link. -

-
-
-
- - checkEmailValidity(value) || "Email is invalid", - }} - render={({ field: { value, onChange, ref } }) => ( - 0} - /> - )} - /> - {resendTimerCode > 0 && ( -

- - We sent the reset link to your email address -

- )} +
+
+
+ Plane Logo + Plane +
+
+
+
+
+
+

+ Reset your password +

+

+ Enter your user account{"'"}s verified email address and we will send you a password reset link. +

- - - Back to sign in - - +
+
+ + checkEmailValidity(value) || "Email is invalid", + }} + render={({ field: { value, onChange, ref } }) => ( + 0} + /> + )} + /> + {resendTimerCode > 0 && ( +

+ + We sent the reset link to your email address +

+ )} +
+ + + Back to sign in + +
+
-
+ ); }; diff --git a/space/pages/accounts/reset-password.tsx b/space/pages/accounts/reset-password.tsx index a8d232d97..773acb10e 100644 --- a/space/pages/accounts/reset-password.tsx +++ b/space/pages/accounts/reset-password.tsx @@ -10,9 +10,11 @@ import { Button, Input } from "@plane/ui"; // components import { PasswordStrengthMeter } from "@/components/accounts"; // helpers +import { EPageTypes } from "@/helpers/authentication.helper"; import { API_BASE_URL } from "@/helpers/common.helper"; import { getPasswordStrength } from "@/helpers/password.helper"; -// hooks +// wrappers +import { AuthWrapper } from "@/lib/wrappers"; // services import { AuthService } from "@/services/authentication.service"; // images @@ -75,98 +77,71 @@ const ResetPasswordPage: NextPage = () => { ); return ( -
-
- Plane background pattern -
-
-
-
- Plane Logo - Plane -
+ +
+
+ Plane background pattern
-
-
-
-
-

- Set new password -

-

Secure your account with a strong password

-
-
- -
- -
- -
+
+
+
+ Plane Logo + Plane +
+
+
+
+
+
+

+ Set new password +

+

Secure your account with a strong password

-
- -
- handleFormChange("password", e.target.value)} - //hasError={Boolean(errors.password)} - placeholder="Enter password" - className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400" - minLength={8} - onFocus={() => setIsPasswordInputFocused(true)} - onBlur={() => setIsPasswordInputFocused(false)} - autoFocus - /> - {showPassword ? ( - setShowPassword(false)} - /> - ) : ( - setShowPassword(true)} - /> - )} -
- {isPasswordInputFocused && } -
- {getPasswordStrength(resetFormData.password) >= 3 && ( + +
-
+
+
handleFormChange("confirm_password", e.target.value)} - placeholder="Confirm password" + name="password" + value={resetFormData.password} + onChange={(e) => handleFormChange("password", e.target.value)} + //hasError={Boolean(errors.password)} + placeholder="Enter password" className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400" + minLength={8} + onFocus={() => setIsPasswordInputFocused(true)} + onBlur={() => setIsPasswordInputFocused(false)} + autoFocus /> {showPassword ? ( { /> )}
- {!!resetFormData.confirm_password && resetFormData.password !== resetFormData.confirm_password && ( - Passwords don{"'"}t match - )} + {isPasswordInputFocused && }
- )} - - + {getPasswordStrength(resetFormData.password) >= 3 && ( +
+ +
+ handleFormChange("confirm_password", e.target.value)} + placeholder="Confirm password" + className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400" + /> + {showPassword ? ( + setShowPassword(false)} + /> + ) : ( + setShowPassword(true)} + /> + )} +
+ {!!resetFormData.confirm_password && + resetFormData.password !== resetFormData.confirm_password && ( + Passwords don{"'"}t match + )} +
+ )} + + +
-
+ ); }; diff --git a/space/pages/index.tsx b/space/pages/index.tsx index 0ab791c60..8bba85cca 100644 --- a/space/pages/index.tsx +++ b/space/pages/index.tsx @@ -2,8 +2,15 @@ import { observer } from "mobx-react-lite"; import { NextPage } from "next"; // components import { AuthView } from "@/components/views"; -// store +// helpers +import { EPageTypes } from "@/helpers/authentication.helper"; +// wrapper +import { AuthWrapper } from "@/lib/wrappers"; -const Index: NextPage = observer(() => ); +const Index: NextPage = observer(() => ( + + + +)); export default Index; diff --git a/space/pages/onboarding/index.tsx b/space/pages/onboarding/index.tsx index acebd6b2c..6ba95126a 100644 --- a/space/pages/onboarding/index.tsx +++ b/space/pages/onboarding/index.tsx @@ -7,8 +7,12 @@ import { useTheme } from "next-themes"; import { Avatar } from "@plane/ui"; // components import { OnBoardingForm } from "@/components/accounts/onboarding-form"; -// mobx +// helpers +import { EPageTypes } from "@/helpers/authentication.helper"; +// hooks import { useUser, useUserProfile } from "@/hooks/store"; +// wrappers +import { AuthWrapper } from "@/lib/wrappers"; // assets import ProfileSetupDark from "public/onboarding/profile-setup-dark.svg"; import ProfileSetup from "public/onboarding/profile-setup.svg"; @@ -49,71 +53,78 @@ const OnBoardingPage = observer(() => { }; return ( -
-
-
-
-
- Plane Logo + +
+
+
+
+
+ Plane Logo +
-
-
-
-
- {user?.avatar && ( - - )} - - {user?.first_name ? `${user?.first_name} ${user?.last_name ?? ""}` : user?.email} - +
+
+
+ {user?.avatar && ( + + )} + + {user?.first_name ? `${user?.first_name} ${user?.last_name ?? ""}` : user?.email} + +
-
-
-
-

Welcome to Plane!

-

- Let’s setup your profile, tell us a bit about yourself. -

-
- -
-
-
-
-
- {user?.avatar && ( - - )} - - {user?.first_name ? `${user?.first_name} ${user?.last_name ?? ""}` : user?.email} - +
+
+

Welcome to Plane!

+

+ Let’s setup your profile, tell us a bit about yourself. +

+
+
-
- Profile setup +
+
+
+ {user?.avatar && ( + + )} + + {user?.first_name ? `${user?.first_name} ${user?.last_name ?? ""}` : user?.email} + +
+
+
+ Profile setup +
-
+ ); }); diff --git a/space/pages/project-not-published/index.tsx b/space/pages/project-not-published/index.tsx index acc638412..803ed3d03 100644 --- a/space/pages/project-not-published/index.tsx +++ b/space/pages/project-not-published/index.tsx @@ -1,32 +1,39 @@ // next imports import Image from "next/image"; -import projectNotPublishedImage from "public/project-not-published.svg"; +// helpers +import { EPageTypes } from "@/helpers/authentication.helper"; +// wrappers +import { AuthWrapper } from "@/lib/wrappers"; +// images +import projectNotPublishedImage from "@/public/project-not-published.svg"; const CustomProjectNotPublishedError = () => ( -
-
-
-
- 404- Page not found + +
+
+
+
+ 404- Page not found +
+
+ Oops! The page you{`'`}re looking for isn{`'`}t live at the moment. +
+
+ If this is your project, login to your workspace to adjust its visibility settings and make it public. +
-
- Oops! The page you{`'`}re looking for isn{`'`}t live at the moment. -
-
- If this is your project, login to your workspace to adjust its visibility settings and make it public. -
-
-
-
+ ); export default CustomProjectNotPublishedError; diff --git a/space/services/api.service.ts b/space/services/api.service.ts index 9ab4d56ed..26f8cc727 100644 --- a/space/services/api.service.ts +++ b/space/services/api.service.ts @@ -19,7 +19,7 @@ abstract class APIService { this.axiosInstance.interceptors.response.use( (response) => response, (error) => { - if (error.response && error.response.status === 401) window.location.href = "/space/login"; + if (error.response && error.response.status === 401) window.location.href = "/"; return Promise.reject(error.response?.data ?? error); } ); diff --git a/space/services/authentication.service.ts b/space/services/authentication.service.ts index 698aaa31f..e18205479 100644 --- a/space/services/authentication.service.ts +++ b/space/services/authentication.service.ts @@ -26,7 +26,7 @@ export class AuthService extends APIService { }); } - async sendResetPasswordLink(data: { email: string }): Promise { + async sendResetPasswordLink(data: { email: string }): Promise { return this.post(`/auth/forgot-password/`, data) .then((response) => response?.data) .catch((error) => { @@ -34,7 +34,7 @@ export class AuthService extends APIService { }); } - async generateUniqueCode(data: { email: string }): Promise { + async generateUniqueCode(data: { email: string }): Promise { return this.post("/auth/spaces/magic-generate/", data, { headers: {} }) .then((response) => response?.data) .catch((error) => { diff --git a/space/services/user.service.ts b/space/services/user.service.ts index a08ccf837..72500a048 100644 --- a/space/services/user.service.ts +++ b/space/services/user.service.ts @@ -18,7 +18,7 @@ export class UserService extends APIService { }); } - async updateUser(data: Partial): Promise { + async updateUser(data: Partial): Promise { return this.patch("/api/users/me/", data) .then((response) => response?.data) .catch((error) => { @@ -33,7 +33,7 @@ export class UserService extends APIService { throw error?.response; }); } - async updateCurrentUserProfile(data: any): Promise { + async updateCurrentUserProfile(data: Partial): Promise { return this.patch("/api/users/me/profile/", data) .then((response) => response?.data) .catch((error) => { diff --git a/web/components/account/auth-forms/sign-in-root.tsx b/web/components/account/auth-forms/auth-root.tsx similarity index 66% rename from web/components/account/auth-forms/sign-in-root.tsx rename to web/components/account/auth-forms/auth-root.tsx index 78a65a63d..a451994d8 100644 --- a/web/components/account/auth-forms/sign-in-root.tsx +++ b/web/components/account/auth-forms/auth-root.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { FC, useEffect, useState } from "react"; import { observer } from "mobx-react"; import { useRouter } from "next/router"; import { IEmailCheckData } from "@plane/types"; @@ -29,26 +29,42 @@ import { AuthService } from "@/services/auth.service"; const authService = new AuthService(); -export const SignInAuthRoot = observer(() => { +type TAuthRoot = { + authMode: EAuthModes; +}; + +export const AuthRoot: FC = observer((props) => { //router const router = useRouter(); - const { email: emailParam, invitation_id, slug: workspaceSlug, error_code, error_message } = router.query; + const { email: emailParam, invitation_id, slug: workspaceSlug, error_code } = router.query; + // props + const { authMode } = props; // states const [authStep, setAuthStep] = useState(EAuthSteps.EMAIL); const [email, setEmail] = useState(emailParam ? emailParam.toString() : ""); const [errorInfo, setErrorInfo] = useState(undefined); // hooks const { instance } = useInstance(); - // derived values - const authMode = EAuthModes.SIGN_IN; useEffect(() => { - if (error_code && error_message) { - const errorhandler = authErrorHandler( - error_code?.toString() as EAuthenticationErrorCodes, - error_message?.toString() - ); + if (error_code) { + const errorhandler = authErrorHandler(error_code?.toString() as EAuthenticationErrorCodes); if (errorhandler) { + if ( + [ + EAuthenticationErrorCodes.AUTHENTICATION_FAILED_SIGN_IN, + EAuthenticationErrorCodes.AUTHENTICATION_FAILED_SIGN_UP, + ].includes(errorhandler.code) + ) + setAuthStep(EAuthSteps.PASSWORD); + if ( + [EAuthenticationErrorCodes.INVALID_MAGIC_CODE, EAuthenticationErrorCodes.EXPIRED_MAGIC_CODE].includes( + errorhandler.code + ) + ) + setAuthStep(EAuthSteps.UNIQUE_CODE); + + // validating wheather to show alert to banner if (errorhandler?.type === EErrorAlertType.TOAST_ALERT) { setToast({ type: TOAST_TYPE.ERROR, @@ -58,23 +74,30 @@ export const SignInAuthRoot = observer(() => { } else setErrorInfo(errorhandler); } } - }, [error_code, error_message]); + }, [error_code, authMode]); // step 1 submit handler- email verification const handleEmailVerification = async (data: IEmailCheckData) => { setEmail(data.email); - await authService - .signInEmailCheck(data) + const emailCheckRequest = + authMode === EAuthModes.SIGN_IN ? authService.signInEmailCheck(data) : authService.signUpEmailCheck(data); + + await emailCheckRequest .then(() => { setAuthStep(EAuthSteps.PASSWORD); }) - .catch((err) => { - setToast({ - type: TOAST_TYPE.ERROR, - title: "Error!", - message: err?.error_message ?? "Something went wrong. Please try again.", - }); + .catch((error) => { + const errorhandler = authErrorHandler(error?.error_code.toString()); + if (errorhandler?.type === EErrorAlertType.BANNER_ALERT) { + setErrorInfo(errorhandler); + return; + } else if (errorhandler?.type === EErrorAlertType.TOAST_ALERT) + setToast({ + type: TOAST_TYPE.ERROR, + title: errorhandler?.title, + message: (errorhandler?.message as string) || "Something went wrong. Please try again.", + }); }); }; @@ -87,7 +110,7 @@ export const SignInAuthRoot = observer(() => { workspaceSlug={workspaceSlug?.toString() || undefined} invitationId={invitation_id?.toString() || undefined} invitationEmail={email || undefined} - authMode={EAuthModes.SIGN_IN} + authMode={authMode} currentAuthStep={authStep} > {errorInfo && errorInfo?.type === EErrorAlertType.BANNER_ALERT && ( diff --git a/web/components/account/auth-forms/index.ts b/web/components/account/auth-forms/index.ts index e5d77069c..604c4b5c6 100644 --- a/web/components/account/auth-forms/index.ts +++ b/web/components/account/auth-forms/index.ts @@ -1,5 +1,4 @@ -export * from "./sign-up-root"; -export * from "./sign-in-root"; +export * from "./auth-root"; export * from "./auth-header"; export * from "./auth-banner"; diff --git a/web/components/account/auth-forms/sign-up-root.tsx b/web/components/account/auth-forms/sign-up-root.tsx deleted file mode 100644 index 0ea698674..000000000 --- a/web/components/account/auth-forms/sign-up-root.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import { FC, useEffect, useState } from "react"; -import { observer } from "mobx-react"; -import { useRouter } from "next/router"; -// types -import { IEmailCheckData } from "@plane/types"; -// ui -import { TOAST_TYPE, setToast } from "@plane/ui"; -// components -import { - AuthHeader, - AuthBanner, - AuthEmailForm, - AuthUniqueCodeForm, - AuthPasswordForm, - OAuthOptions, - TermsAndConditions, -} from "@/components/account"; -// helpers -import { - EAuthModes, - EAuthSteps, - EAuthenticationErrorCodes, - EErrorAlertType, - TAuthErrorInfo, - authErrorHandler, -} from "@/helpers/authentication.helper"; -// hooks -import { useInstance } from "@/hooks/store"; -// services -import { AuthService } from "@/services/auth.service"; - -// service initialization -const authService = new AuthService(); - -export const SignUpAuthRoot: FC = observer(() => { - //router - const router = useRouter(); - const { email: emailParam, invitation_id, slug: workspaceSlug, error_code, error_message } = router.query; - // states - const [authStep, setAuthStep] = useState(EAuthSteps.EMAIL); - const [email, setEmail] = useState(emailParam ? emailParam.toString() : ""); - const [errorInfo, setErrorInfo] = useState(undefined); - // hooks - const { instance } = useInstance(); - // derived values - const authMode = EAuthModes.SIGN_UP; - const isSmtpConfigured = instance?.config?.is_smtp_configured; - - useEffect(() => { - if (error_code && error_message) { - const errorhandler = authErrorHandler( - error_code?.toString() as EAuthenticationErrorCodes, - error_message?.toString() - ); - if (errorhandler) { - if (errorhandler?.type === EErrorAlertType.TOAST_ALERT) { - setToast({ - type: TOAST_TYPE.ERROR, - title: errorhandler?.title, - message: errorhandler?.message as string, - }); - } else setErrorInfo(errorhandler); - } - } - }, [error_code, error_message]); - - // email verification - const handleEmailVerification = async (data: IEmailCheckData) => { - setEmail(data.email); - await authService - .signUpEmailCheck(data) - .then(() => { - if (isSmtpConfigured) setAuthStep(EAuthSteps.UNIQUE_CODE); - else setAuthStep(EAuthSteps.PASSWORD); - }) - .catch((error) => { - const errorhandler = authErrorHandler(error?.error_code, error?.error_message); - if (errorhandler) { - setErrorInfo(errorhandler); - return; - } - setToast({ - type: TOAST_TYPE.ERROR, - title: "Error!", - message: error?.error_message ?? "Something went wrong. Please try again.", - }); - }); - }; - - const isOAuthEnabled = - instance?.config && (instance?.config?.is_google_enabled || instance?.config?.is_github_enabled); - - return ( -
- - {errorInfo && errorInfo?.type === EErrorAlertType.BANNER_ALERT && ( - setErrorInfo(value)} /> - )} - {authStep === EAuthSteps.EMAIL && } - {authStep === EAuthSteps.UNIQUE_CODE && ( - { - setEmail(""); - setAuthStep(EAuthSteps.EMAIL); - }} - submitButtonText="Continue" - mode={authMode} - /> - )} - {authStep === EAuthSteps.PASSWORD && ( - { - setEmail(""); - setAuthStep(EAuthSteps.EMAIL); - }} - handleStepChange={(step) => setAuthStep(step)} - mode={authMode} - /> - )} - {isOAuthEnabled && } - - -
- ); -}); diff --git a/web/helpers/authentication.helper.ts b/web/helpers/authentication.helper.ts deleted file mode 100644 index 375d2a9fa..000000000 --- a/web/helpers/authentication.helper.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { ReactNode } from "react"; - -export enum EPageTypes { - "PUBLIC" = "PUBLIC", - "NON_AUTHENTICATED" = "NON_AUTHENTICATED", - "ONBOARDING" = "ONBOARDING", - "AUTHENTICATED" = "AUTHENTICATED", -} - -export enum EAuthModes { - SIGN_IN = "SIGN_IN", - SIGN_UP = "SIGN_UP", -} - -export enum EAuthSteps { - EMAIL = "EMAIL", - PASSWORD = "PASSWORD", - UNIQUE_CODE = "UNIQUE_CODE", -} - -export enum EAuthenticationErrorCodes { - // alert errors - INSTANCE_NOT_CONFIGURED = "INSTANCE_NOT_CONFIGURED", - SMTP_NOT_CONFIGURED = "SMTP_NOT_CONFIGURED", - AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED", - INVALID_TOKEN = "INVALID_TOKEN", - EXPIRED_TOKEN = "EXPIRED_TOKEN", - IMPROPERLY_CONFIGURED = "IMPROPERLY_CONFIGURED", - OAUTH_PROVIDER_ERROR = "OAUTH_PROVIDER_ERROR", - // banner errors - INVALID_EMAIL = "INVALID_EMAIL", - INVALID_PASSWORD = "INVALID_PASSWORD", - USER_DOES_NOT_EXIST = "USER_DOES_NOT_EXIST", - ADMIN_ALREADY_EXIST = "ADMIN_ALREADY_EXIST", - USER_ALREADY_EXIST = "USER_ALREADY_EXIST", - // inline errors from backend - REQUIRED_EMAIL_PASSWORD_FIRST_NAME = "REQUIRED_EMAIL_PASSWORD_FIRST_NAME", - REQUIRED_EMAIL_PASSWORD = "REQUIRED_EMAIL_PASSWORD", - EMAIL_CODE_REQUIRED = "EMAIL_CODE_REQUIRED", -} - -export enum EErrorAlertType { - BANNER_ALERT = "BANNER_ALERT", - TOAST_ALERT = "TOAST_ALERT", - INLINE_FIRST_NAME = "INLINE_FIRST_NAME", - INLINE_EMAIL = "INLINE_EMAIL", - INLINE_PASSWORD = "INLINE_PASSWORD", - INLINE_EMAIL_CODE = "INLINE_EMAIL_CODE", -} - -export type TAuthErrorInfo = { type: EErrorAlertType; title: string; message: ReactNode }; - -const errorCodeMessages: { [key in EAuthenticationErrorCodes]: { title: string; message: ReactNode } } = { - [EAuthenticationErrorCodes.INSTANCE_NOT_CONFIGURED]: { - title: `Instance not configured`, - message: `Instance not configured. Please contact your administrator.`, - }, - [EAuthenticationErrorCodes.SMTP_NOT_CONFIGURED]: { - title: `SMTP not configured`, - message: `SMTP not configured. Please contact your administrator.`, - }, - [EAuthenticationErrorCodes.AUTHENTICATION_FAILED]: { - title: `Authentication failed.`, - message: `Authentication failed. Please try again.`, - }, - [EAuthenticationErrorCodes.INVALID_TOKEN]: { title: `Invalid token.`, message: `Invalid token. Please try again.` }, - [EAuthenticationErrorCodes.EXPIRED_TOKEN]: { title: `Expired token.`, message: `Expired token. Please try again.` }, - [EAuthenticationErrorCodes.IMPROPERLY_CONFIGURED]: { - title: `Improperly configured.`, - message: `Improperly configured. Please contact your administrator.`, - }, - [EAuthenticationErrorCodes.OAUTH_PROVIDER_ERROR]: { - title: `OAuth provider error.`, - message: `OAuth provider error. Please try again.`, - }, - [EAuthenticationErrorCodes.INVALID_EMAIL]: { - title: `Invalid email.`, - message: `Invalid email. Please try again.`, - }, - [EAuthenticationErrorCodes.INVALID_PASSWORD]: { - title: `Invalid password.`, - message: `Invalid password. Please try again.`, - }, - [EAuthenticationErrorCodes.USER_DOES_NOT_EXIST]: { - title: `User does not exist.`, - message: `User does not exist. Please try again.`, - }, - [EAuthenticationErrorCodes.ADMIN_ALREADY_EXIST]: { - title: `Admin already exists.`, - message: `Admin already exists. Please try again.`, - }, - [EAuthenticationErrorCodes.USER_ALREADY_EXIST]: { - title: `User already exists.`, - message: `User already exists. Please try again.`, - }, - [EAuthenticationErrorCodes.REQUIRED_EMAIL_PASSWORD_FIRST_NAME]: { - title: `Missing fields.`, - message: `Email, password, and first name are required.`, - }, - [EAuthenticationErrorCodes.REQUIRED_EMAIL_PASSWORD]: { - title: `Missing fields.`, - message: `Email and password are required.`, - }, - [EAuthenticationErrorCodes.EMAIL_CODE_REQUIRED]: { - title: `Missing fields.`, - message: `Email and code are required.`, - }, -}; - -export const authErrorHandler = ( - errorCode: EAuthenticationErrorCodes, - errorMessage: string | undefined -): TAuthErrorInfo | undefined => { - const toastAlertErrorCodes = [ - EAuthenticationErrorCodes.INSTANCE_NOT_CONFIGURED, - EAuthenticationErrorCodes.SMTP_NOT_CONFIGURED, - EAuthenticationErrorCodes.AUTHENTICATION_FAILED, - EAuthenticationErrorCodes.INVALID_TOKEN, - EAuthenticationErrorCodes.EXPIRED_TOKEN, - EAuthenticationErrorCodes.IMPROPERLY_CONFIGURED, - EAuthenticationErrorCodes.OAUTH_PROVIDER_ERROR, - ]; - const bannerAlertErrorCodes = [ - EAuthenticationErrorCodes.INVALID_EMAIL, - EAuthenticationErrorCodes.INVALID_PASSWORD, - EAuthenticationErrorCodes.USER_DOES_NOT_EXIST, - EAuthenticationErrorCodes.ADMIN_ALREADY_EXIST, - EAuthenticationErrorCodes.USER_ALREADY_EXIST, - EAuthenticationErrorCodes.REQUIRED_EMAIL_PASSWORD_FIRST_NAME, - EAuthenticationErrorCodes.REQUIRED_EMAIL_PASSWORD, - EAuthenticationErrorCodes.EMAIL_CODE_REQUIRED, - ]; - - if (toastAlertErrorCodes.includes(errorCode)) - return { - type: EErrorAlertType.TOAST_ALERT, - title: errorCodeMessages[errorCode]?.title || "Error", - message: errorMessage || errorCodeMessages[errorCode]?.message || "Something went wrong. Please try again.", - }; - - if (bannerAlertErrorCodes.includes(errorCode)) - return { - type: EErrorAlertType.BANNER_ALERT, - title: errorCodeMessages[errorCode]?.title || "Error", - message: errorMessage || errorCodeMessages[errorCode]?.message || "Something went wrong. Please try again.", - }; - - return undefined; -}; diff --git a/web/helpers/authentication.helper.tsx b/web/helpers/authentication.helper.tsx new file mode 100644 index 000000000..7bd61eb83 --- /dev/null +++ b/web/helpers/authentication.helper.tsx @@ -0,0 +1,290 @@ +import { ReactNode } from "react"; +import Link from "next/link"; + +export enum EPageTypes { + "PUBLIC" = "PUBLIC", + "NON_AUTHENTICATED" = "NON_AUTHENTICATED", + "ONBOARDING" = "ONBOARDING", + "AUTHENTICATED" = "AUTHENTICATED", +} + +export enum EAuthModes { + SIGN_IN = "SIGN_IN", + SIGN_UP = "SIGN_UP", +} + +export enum EAuthSteps { + EMAIL = "EMAIL", + PASSWORD = "PASSWORD", + UNIQUE_CODE = "UNIQUE_CODE", +} + +export enum EAuthenticationErrorCodes { + INSTANCE_NOT_CONFIGURED = "5000", + SIGNUP_DISABLED = "5001", + INVALID_PASSWORD = "5002", + USER_ALREADY_EXIST = "5003", + USER_DOES_NOT_EXIST = "5004", + AUTHENTICATION_FAILED_SIGN_IN = "5005", + AUTHENTICATION_FAILED_SIGN_UP = "5006", + SMTP_NOT_CONFIGURED = "5007", + INVALID_MAGIC_CODE = "5008", + EXPIRED_MAGIC_CODE = "5009", + GOOGLE_NOT_CONFIGURED = "5010", + GITHUB_NOT_CONFIGURED = "5011", + INVALID_EMAIL = "5012", + EMAIL_REQUIRED = "5013", + REQUIRED_EMAIL_PASSWORD_SIGN_IN = "5014", + INVALID_EMAIL_SIGN_IN = "5015", + INVALID_EMAIL_SIGN_UP = "5016", + INVALID_EMAIL_MAGIC_SIGN_IN = "5017", + INVALID_EMAIL_MAGIC_SIGN_UP = "5018", + GITHUB_OAUTH_PROVIDER_ERROR = "5019", + GOOGLE_OAUTH_PROVIDER_ERROR = "5020", + MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED = "5021", + MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED = "5022", + INVALID_PASSWORD_TOKEN = "5023", + EXPIRED_PASSWORD_TOKEN = "5024", + INCORRECT_OLD_PASSWORD = "5025", + INVALID_NEW_PASSWORD = "5026", + PASSWORD_ALREADY_SET = "5027", + ADMIN_ALREADY_EXIST = "5028", + REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME = "5029", + INVALID_ADMIN_EMAIL = "5030", + INVALID_ADMIN_PASSWORD = "5031", + REQUIRED_ADMIN_EMAIL_PASSWORD = "5032", + ADMIN_AUTHENTICATION_FAILED = "5034", +} + +export enum EErrorAlertType { + BANNER_ALERT = "BANNER_ALERT", + TOAST_ALERT = "TOAST_ALERT", + INLINE_FIRST_NAME = "INLINE_FIRST_NAME", + INLINE_EMAIL = "INLINE_EMAIL", + INLINE_PASSWORD = "INLINE_PASSWORD", + INLINE_EMAIL_CODE = "INLINE_EMAIL_CODE", +} + +export type TAuthErrorInfo = { + type: EErrorAlertType; + code: EAuthenticationErrorCodes; + title: string; + message: ReactNode; +}; + +const errorCodeMessages: { [key in EAuthenticationErrorCodes]: { title: string; message: ReactNode } } = { + [EAuthenticationErrorCodes.INSTANCE_NOT_CONFIGURED]: { + title: `Instance not configured`, + message: `Instance not configured. Please contact your administrator.`, + }, + [EAuthenticationErrorCodes.SIGNUP_DISABLED]: { + title: `Sign up disabled`, + message: `Sign up disabled. Please contact your administrator.`, + }, + [EAuthenticationErrorCodes.INVALID_PASSWORD]: { + title: `Invalid password`, + message: `Invalid password. Please try again.`, + }, + [EAuthenticationErrorCodes.USER_ALREADY_EXIST]: { + title: `User already exists`, + message: ( +
+ Your account is already registered.  + + Sign In + +  now. +
+ ), + }, + [EAuthenticationErrorCodes.USER_DOES_NOT_EXIST]: { + title: `User does not exist`, + message: ( +
+ No account found.  + + Create one + +  to get started. +
+ ), + }, + [EAuthenticationErrorCodes.AUTHENTICATION_FAILED_SIGN_IN]: { + title: `Authentication failed`, + message: `Authentication failed. Please try again.`, + }, + [EAuthenticationErrorCodes.AUTHENTICATION_FAILED_SIGN_UP]: { + title: `Authentication failed`, + message: `Authentication failed. Please try again.`, + }, + [EAuthenticationErrorCodes.SMTP_NOT_CONFIGURED]: { + title: `SMTP not configured`, + message: `SMTP not configured. Please contact your administrator.`, + }, + [EAuthenticationErrorCodes.INVALID_MAGIC_CODE]: { + title: `Authentication failed`, + message: `Invalid magic code. Please try again.`, + }, + [EAuthenticationErrorCodes.EXPIRED_MAGIC_CODE]: { + title: `Expired magic code`, + message: `Expired magic code. Please try again.`, + }, + [EAuthenticationErrorCodes.GOOGLE_NOT_CONFIGURED]: { + title: `Google not configured`, + message: `Google not configured. Please contact your administrator.`, + }, + [EAuthenticationErrorCodes.GITHUB_NOT_CONFIGURED]: { + title: `GitHub not configured`, + message: `GitHub not configured. Please contact your administrator.`, + }, + [EAuthenticationErrorCodes.INVALID_EMAIL]: { + title: `Invalid email`, + message: `Invalid email. Please try again.`, + }, + [EAuthenticationErrorCodes.EMAIL_REQUIRED]: { + title: `Email required`, + message: `Email required. Please try again.`, + }, + [EAuthenticationErrorCodes.REQUIRED_EMAIL_PASSWORD_SIGN_IN]: { + title: `Email and password required`, + message: `Email and password required. Please try again.`, + }, + [EAuthenticationErrorCodes.INVALID_EMAIL_SIGN_IN]: { + title: `Invalid email`, + message: `Invalid email. Please try again.`, + }, + [EAuthenticationErrorCodes.INVALID_EMAIL_SIGN_UP]: { + title: `Invalid email`, + message: `Invalid email. Please try again.`, + }, + [EAuthenticationErrorCodes.INVALID_EMAIL_MAGIC_SIGN_IN]: { + title: `Invalid email`, + message: `Invalid email. Please try again.`, + }, + [EAuthenticationErrorCodes.INVALID_EMAIL_MAGIC_SIGN_UP]: { + title: `Invalid email`, + message: `Invalid email. Please try again.`, + }, + [EAuthenticationErrorCodes.GITHUB_OAUTH_PROVIDER_ERROR]: { + title: `GitHub OAuth provider error`, + message: `GitHub OAuth provider error. Please try again.`, + }, + [EAuthenticationErrorCodes.GOOGLE_OAUTH_PROVIDER_ERROR]: { + title: `Google OAuth provider error`, + message: `Google OAuth provider error. Please try again.`, + }, + [EAuthenticationErrorCodes.MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED]: { + title: `Email and code required`, + message: `Email and code required. Please try again.`, + }, + [EAuthenticationErrorCodes.MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED]: { + title: `Email and code required`, + message: `Email and code required. Please try again.`, + }, + [EAuthenticationErrorCodes.INVALID_PASSWORD_TOKEN]: { + title: `Invalid password token`, + message: `Invalid password token. Please try again.`, + }, + [EAuthenticationErrorCodes.EXPIRED_PASSWORD_TOKEN]: { + title: `Expired password token`, + message: `Expired password token. Please try again.`, + }, + [EAuthenticationErrorCodes.INCORRECT_OLD_PASSWORD]: { + title: `Incorrect old password`, + message: `Incorrect old password. Please try again.`, + }, + [EAuthenticationErrorCodes.INVALID_NEW_PASSWORD]: { + title: `Invalid new password`, + message: `Invalid new password. Please try again.`, + }, + [EAuthenticationErrorCodes.PASSWORD_ALREADY_SET]: { + title: `Password already set`, + message: `Password already set. Please try again.`, + }, + [EAuthenticationErrorCodes.ADMIN_ALREADY_EXIST]: { + title: `Admin already exists`, + message: `Admin already exists. Please try again.`, + }, + [EAuthenticationErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME]: { + title: `Email, password and first name required`, + message: `Email, password and first name required. Please try again.`, + }, + [EAuthenticationErrorCodes.INVALID_ADMIN_EMAIL]: { + title: `Invalid email`, + message: `Invalid email. Please try again.`, + }, + [EAuthenticationErrorCodes.INVALID_ADMIN_PASSWORD]: { + title: `Invalid password`, + message: `Invalid password. Please try again.`, + }, + [EAuthenticationErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD]: { + title: `Email and password required`, + message: `Email and password required. Please try again.`, + }, + [EAuthenticationErrorCodes.ADMIN_AUTHENTICATION_FAILED]: { + title: `Authentication failed`, + message: `Authentication failed. Please try again.`, + }, +}; + +export const authErrorHandler = (errorCode: EAuthenticationErrorCodes): TAuthErrorInfo | undefined => { + const toastAlertErrorCodes = [ + EAuthenticationErrorCodes.INSTANCE_NOT_CONFIGURED, + EAuthenticationErrorCodes.SIGNUP_DISABLED, + EAuthenticationErrorCodes.INVALID_PASSWORD, + EAuthenticationErrorCodes.AUTHENTICATION_FAILED_SIGN_IN, + EAuthenticationErrorCodes.AUTHENTICATION_FAILED_SIGN_UP, + EAuthenticationErrorCodes.SMTP_NOT_CONFIGURED, + EAuthenticationErrorCodes.INVALID_MAGIC_CODE, + EAuthenticationErrorCodes.EXPIRED_MAGIC_CODE, + EAuthenticationErrorCodes.GOOGLE_NOT_CONFIGURED, + EAuthenticationErrorCodes.GITHUB_NOT_CONFIGURED, + EAuthenticationErrorCodes.INVALID_EMAIL, + EAuthenticationErrorCodes.EMAIL_REQUIRED, + EAuthenticationErrorCodes.REQUIRED_EMAIL_PASSWORD_SIGN_IN, + EAuthenticationErrorCodes.INVALID_EMAIL_SIGN_IN, + EAuthenticationErrorCodes.INVALID_EMAIL_SIGN_UP, + EAuthenticationErrorCodes.INVALID_EMAIL_MAGIC_SIGN_IN, + EAuthenticationErrorCodes.INVALID_EMAIL_MAGIC_SIGN_UP, + EAuthenticationErrorCodes.GITHUB_OAUTH_PROVIDER_ERROR, + EAuthenticationErrorCodes.GOOGLE_OAUTH_PROVIDER_ERROR, + EAuthenticationErrorCodes.MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED, + EAuthenticationErrorCodes.MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED, + EAuthenticationErrorCodes.INVALID_PASSWORD_TOKEN, + EAuthenticationErrorCodes.EXPIRED_PASSWORD_TOKEN, + EAuthenticationErrorCodes.INCORRECT_OLD_PASSWORD, + EAuthenticationErrorCodes.INVALID_NEW_PASSWORD, + EAuthenticationErrorCodes.PASSWORD_ALREADY_SET, + EAuthenticationErrorCodes.ADMIN_ALREADY_EXIST, + EAuthenticationErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME, + EAuthenticationErrorCodes.INVALID_ADMIN_EMAIL, + EAuthenticationErrorCodes.INVALID_ADMIN_PASSWORD, + EAuthenticationErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD, + EAuthenticationErrorCodes.ADMIN_AUTHENTICATION_FAILED, + ]; + const bannerAlertErrorCodes = [ + EAuthenticationErrorCodes.USER_ALREADY_EXIST, + EAuthenticationErrorCodes.USER_DOES_NOT_EXIST, + ]; + + if (toastAlertErrorCodes.includes(errorCode)) + return { + type: EErrorAlertType.TOAST_ALERT, + code: errorCode, + title: errorCodeMessages[errorCode]?.title || "Error", + message: errorCodeMessages[errorCode]?.message || "Something went wrong. Please try again.", + }; + + if (bannerAlertErrorCodes.includes(errorCode)) + return { + type: EErrorAlertType.BANNER_ALERT, + code: errorCode, + title: errorCodeMessages[errorCode]?.title || "Error", + message: errorCodeMessages[errorCode]?.message || "Something went wrong. Please try again.", + }; + + return undefined; +}; diff --git a/web/lib/wrappers/instance-wrapper.tsx b/web/lib/wrappers/instance-wrapper.tsx index 25ca7c424..a9d114dbc 100644 --- a/web/lib/wrappers/instance-wrapper.tsx +++ b/web/lib/wrappers/instance-wrapper.tsx @@ -30,12 +30,7 @@ export const InstanceWrapper: FC = observer((props) => { ); // something went wrong while in the request - if (error && error?.status === "error") - return ( -
- Something went wrong. please try again later -
- ); + if (error && error?.status === "error") return <>{children}; // instance is not ready and setup is not done if (instance?.instance?.is_setup_done === false) return ; diff --git a/web/pages/accounts/forgot-password.tsx b/web/pages/accounts/forgot-password.tsx index 57debc226..cb1df8504 100644 --- a/web/pages/accounts/forgot-password.tsx +++ b/web/pages/accounts/forgot-password.tsx @@ -122,7 +122,7 @@ const ForgotPasswordPage: NextPageWithLayout = () => {
-
+

@@ -176,7 +176,7 @@ const ForgotPasswordPage: NextPageWithLayout = () => { > {resendTimerCode > 0 ? `Resend in ${resendTimerCode} seconds` : "Send reset link"} - + Back to sign in diff --git a/web/pages/accounts/reset-password.tsx b/web/pages/accounts/reset-password.tsx index 433689bf4..f7c9acf37 100644 --- a/web/pages/accounts/reset-password.tsx +++ b/web/pages/accounts/reset-password.tsx @@ -90,7 +90,7 @@ const ResetPasswordPage: NextPageWithLayout = () => { Plane

-
+

diff --git a/web/pages/accounts/set-password.tsx b/web/pages/accounts/set-password.tsx index 86f98b272..7437f8ea9 100644 --- a/web/pages/accounts/set-password.tsx +++ b/web/pages/accounts/set-password.tsx @@ -111,7 +111,7 @@ const SetPasswordPage: NextPageWithLayout = observer(() => { Plane

-
+

diff --git a/web/pages/accounts/sign-in.tsx b/web/pages/accounts/sign-in.tsx index 30a5b96cb..11f7cc478 100644 --- a/web/pages/accounts/sign-in.tsx +++ b/web/pages/accounts/sign-in.tsx @@ -4,12 +4,12 @@ import Link from "next/link"; // ui import { useTheme } from "next-themes"; // components -import { SignInAuthRoot } from "@/components/account"; +import { AuthRoot } from "@/components/account"; import { PageHead } from "@/components/core"; // constants import { NAVIGATE_TO_SIGNUP } from "@/constants/event-tracker"; // helpers -import { EPageTypes } from "@/helpers/authentication.helper"; +import { EAuthModes, EPageTypes } from "@/helpers/authentication.helper"; // hooks import { useEventTracker } from "@/hooks/store"; // layouts @@ -58,8 +58,8 @@ const SignInPage: NextPageWithLayout = observer(() => {

-
- +
+
diff --git a/web/pages/index.tsx b/web/pages/index.tsx index 5b23b618e..6b085ffd3 100644 --- a/web/pages/index.tsx +++ b/web/pages/index.tsx @@ -5,12 +5,12 @@ import Link from "next/link"; // ui import { useTheme } from "next-themes"; // components -import { SignUpAuthRoot } from "@/components/account"; +import { AuthRoot } from "@/components/account"; import { PageHead } from "@/components/core"; // constants import { NAVIGATE_TO_SIGNIN } from "@/constants/event-tracker"; // helpers -import { EPageTypes } from "@/helpers/authentication.helper"; +import { EAuthModes, EPageTypes } from "@/helpers/authentication.helper"; // hooks import { useEventTracker } from "@/hooks/store"; // layouts @@ -56,8 +56,8 @@ const HomePage: NextPageWithLayout = observer(() => {
-
- +
+