chore: handled authentication in space and web apps

This commit is contained in:
guru_sainath 2024-05-08 12:25:36 +05:30
parent 61f47258ee
commit f0faf028c2
52 changed files with 1089 additions and 681 deletions

View File

@ -120,7 +120,7 @@ class MagicCodeProvider(CredentialAdapter):
"INVALID_MAGIC_CODE" "INVALID_MAGIC_CODE"
], ],
error_message="INVALID_MAGIC_CODE", error_message="INVALID_MAGIC_CODE",
payload={"email": str(self.key)}, payload={"email": str(email)},
) )
else: else:
raise AuthenticationException( raise AuthenticationException(

View File

@ -8,7 +8,7 @@ import { Button, Input, Spinner, TOAST_TYPE, setToast } from "@plane/ui";
// components // components
import { UserImageUploadModal } from "@/components/accounts"; import { UserImageUploadModal } from "@/components/accounts";
// hooks // hooks
import { useMobxStore } from "@/lib/mobx/store-provider"; import { useMobxStore } from "@/hooks/store";
// services // services
import fileService from "@/services/file.service"; import fileService from "@/services/file.service";

View File

@ -6,7 +6,7 @@ import { useRouter } from "next/router";
import { IssueBlockDueDate } from "@/components/issues/board-views/block-due-date"; import { IssueBlockDueDate } from "@/components/issues/board-views/block-due-date";
import { IssueBlockPriority } from "@/components/issues/board-views/block-priority"; import { IssueBlockPriority } from "@/components/issues/board-views/block-priority";
import { IssueBlockState } from "@/components/issues/board-views/block-state"; import { IssueBlockState } from "@/components/issues/board-views/block-state";
import { useMobxStore } from "@/lib/mobx/store-provider"; import { useMobxStore } from "@/hooks/store";
// components // components
// interfaces // interfaces

View File

@ -6,7 +6,7 @@ import { StateGroupIcon } from "@plane/ui";
import { issueGroupFilter } from "@/constants/data"; import { issueGroupFilter } from "@/constants/data";
// ui // ui
// mobx hook // mobx hook
import { useMobxStore } from "@/lib/mobx/store-provider"; import { useMobxStore } from "@/hooks/store";
import { RootStore } from "@/store/root.store"; import { RootStore } from "@/store/root.store";
import { IIssueState } from "types/issue"; import { IIssueState } from "types/issue";

View File

@ -9,7 +9,7 @@ import { IssueKanBanHeader } from "@/components/issues/board-views/kanban/header
import { Icon } from "@/components/ui"; import { Icon } from "@/components/ui";
// interfaces // interfaces
// mobx hook // mobx hook
import { useMobxStore } from "@/lib/mobx/store-provider"; import { useMobxStore } from "@/hooks/store";
import { RootStore } from "@/store/root.store"; import { RootStore } from "@/store/root.store";
import { IIssueState, IIssue } from "types/issue"; import { IIssueState, IIssue } from "types/issue";

View File

@ -7,7 +7,7 @@ import { IssueBlockLabels } from "@/components/issues/board-views/block-labels";
import { IssueBlockPriority } from "@/components/issues/board-views/block-priority"; import { IssueBlockPriority } from "@/components/issues/board-views/block-priority";
import { IssueBlockState } from "@/components/issues/board-views/block-state"; import { IssueBlockState } from "@/components/issues/board-views/block-state";
// mobx hook // mobx hook
import { useMobxStore } from "@/lib/mobx/store-provider"; import { useMobxStore } from "@/hooks/store";
// interfaces // interfaces
import { RootStore } from "@/store/root.store"; import { RootStore } from "@/store/root.store";
import { IIssue } from "types/issue"; import { IIssue } from "types/issue";

View File

@ -6,7 +6,7 @@ import { StateGroupIcon } from "@plane/ui";
// constants // constants
import { issueGroupFilter } from "@/constants/data"; import { issueGroupFilter } from "@/constants/data";
// mobx hook // mobx hook
import { useMobxStore } from "@/lib/mobx/store-provider"; import { useMobxStore } from "@/hooks/store";
import { RootStore } from "@/store/root.store"; import { RootStore } from "@/store/root.store";
import { IIssueState } from "types/issue"; import { IIssueState } from "types/issue";

View File

@ -4,7 +4,7 @@ import { IssueListBlock } from "@/components/issues/board-views/list/block";
import { IssueListHeader } from "@/components/issues/board-views/list/header"; import { IssueListHeader } from "@/components/issues/board-views/list/header";
// interfaces // interfaces
// mobx hook // mobx hook
import { useMobxStore } from "@/lib/mobx/store-provider"; import { useMobxStore } from "@/hooks/store";
// store // store
import { RootStore } from "@/store/root.store"; import { RootStore } from "@/store/root.store";
import { IIssueState, IIssue } from "types/issue"; import { IIssueState, IIssue } from "types/issue";

View File

@ -3,7 +3,7 @@ import { observer } from "mobx-react-lite";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// components // components
// store // store
import { useMobxStore } from "@/lib/mobx/store-provider"; import { useMobxStore } from "@/hooks/store";
import { IIssueFilterOptions } from "@/store/issues/types"; import { IIssueFilterOptions } from "@/store/issues/types";
import { RootStore } from "@/store/root.store"; import { RootStore } from "@/store/root.store";
import { AppliedFiltersList } from "./filters-list"; import { AppliedFiltersList } from "./filters-list";

View File

@ -2,7 +2,7 @@ import { FC, useCallback } from "react";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// components // components
import { useMobxStore } from "@/lib/mobx/store-provider"; import { useMobxStore } from "@/hooks/store";
import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "@/store/issues/helpers"; import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "@/store/issues/helpers";
import { IIssueFilterOptions } from "@/store/issues/types"; import { IIssueFilterOptions } from "@/store/issues/types";
import { RootStore } from "@/store/root.store"; import { RootStore } from "@/store/root.store";

View File

@ -8,8 +8,7 @@ import { Avatar, Button } from "@plane/ui";
import { ProjectLogo } from "@/components/common"; import { ProjectLogo } from "@/components/common";
import { IssueFiltersDropdown } from "@/components/issues/filters"; import { IssueFiltersDropdown } from "@/components/issues/filters";
// hooks // hooks
import { useUser } from "@/hooks/store"; import { useMobxStore, useUser } from "@/hooks/store";
import { useMobxStore } from "@/lib/mobx/store-provider";
// store // store
import { RootStore } from "@/store/root.store"; import { RootStore } from "@/store/root.store";
import { TIssueBoardKeys } from "@/types/issue"; import { TIssueBoardKeys } from "@/types/issue";

View File

@ -3,7 +3,7 @@ import { useRouter } from "next/router";
// constants // constants
import { issueViews } from "@/constants/data"; import { issueViews } from "@/constants/data";
// mobx // mobx
import { useMobxStore } from "@/lib/mobx/store-provider"; import { useMobxStore } from "@/hooks/store";
import { RootStore } from "@/store/root.store"; import { RootStore } from "@/store/root.store";
import { TIssueBoardKeys } from "types/issue"; import { TIssueBoardKeys } from "types/issue";

View File

@ -6,10 +6,8 @@ import { useForm, Controller } from "react-hook-form";
import { EditorRefApi } from "@plane/lite-text-editor"; import { EditorRefApi } from "@plane/lite-text-editor";
import { LiteTextEditor } from "@/components/editor/lite-text-editor"; import { LiteTextEditor } from "@/components/editor/lite-text-editor";
// hooks // hooks
import { useUser } from "@/hooks/store"; import { useMobxStore, useUser } from "@/hooks/store";
import useToast from "@/hooks/use-toast"; import useToast from "@/hooks/use-toast";
// lib
import { useMobxStore } from "@/lib/mobx/store-provider";
// types // types
import { Comment } from "@/types/issue"; import { Comment } from "@/types/issue";

View File

@ -10,9 +10,7 @@ import { CommentReactions } from "@/components/issues/peek-overview";
// helpers // helpers
import { timeAgo } from "@/helpers/date-time.helper"; import { timeAgo } from "@/helpers/date-time.helper";
// hooks // hooks
import { useUser } from "@/hooks/store"; import { useMobxStore, useUser } from "@/hooks/store";
// mobx store
import { useMobxStore } from "@/lib/mobx/store-provider";
// store // store
import { RootStore } from "@/store/root.store"; import { RootStore } from "@/store/root.store";
// types // types

View File

@ -1,16 +1,13 @@
import React from "react"; import React from "react";
// mobx
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// ui
import { Tooltip } from "@plane/ui"; import { Tooltip } from "@plane/ui";
// ui
import { ReactionSelector } from "@/components/ui"; import { ReactionSelector } from "@/components/ui";
// helpers // helpers
import { groupReactions, renderEmoji } from "@/helpers/emoji.helper"; import { groupReactions, renderEmoji } from "@/helpers/emoji.helper";
// hooks // hooks
import { useUser } from "@/hooks/store"; import { useMobxStore, useUser } from "@/hooks/store";
import { useMobxStore } from "@/lib/mobx/store-provider";
type Props = { type Props = {
commentId: string; commentId: string;

View File

@ -8,7 +8,7 @@ import { Icon } from "@/components/ui";
// helpers // helpers
import { copyTextToClipboard } from "@/helpers/string.helper"; import { copyTextToClipboard } from "@/helpers/string.helper";
// store // store
import { useMobxStore } from "@/lib/mobx/store-provider"; import { useMobxStore } from "@/hooks/store";
import { IPeekMode } from "@/store/issue_details"; import { IPeekMode } from "@/store/issue_details";
import { RootStore } from "@/store/root.store"; import { RootStore } from "@/store/root.store";
// lib // lib

View File

@ -2,13 +2,12 @@ import React from "react";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// lib
import { Button } from "@plane/ui"; import { Button } from "@plane/ui";
// components
import { CommentCard, AddComment } from "@/components/issues/peek-overview"; import { CommentCard, AddComment } from "@/components/issues/peek-overview";
import { Icon } from "@/components/ui"; import { Icon } from "@/components/ui";
// hooks // hooks
import { useUser } from "@/hooks/store"; import { useMobxStore, useUser } from "@/hooks/store";
import { useMobxStore } from "@/lib/mobx/store-provider";
// types // types
import { IIssue } from "@/types/issue"; import { IIssue } from "@/types/issue";

View File

@ -4,12 +4,10 @@ import { useRouter } from "next/router";
// lib // lib
import { Tooltip } from "@plane/ui"; import { Tooltip } from "@plane/ui";
import { ReactionSelector } from "@/components/ui"; import { ReactionSelector } from "@/components/ui";
// helpers
import { groupReactions, renderEmoji } from "@/helpers/emoji.helper"; import { groupReactions, renderEmoji } from "@/helpers/emoji.helper";
// hooks // hooks
import { useUser } from "@/hooks/store"; import { useMobxStore, useUser } from "@/hooks/store";
import { useMobxStore } from "@/lib/mobx/store-provider";
// helpers
// components
export const IssueEmojiReactions: React.FC = observer(() => { export const IssueEmojiReactions: React.FC = observer(() => {
// router // router

View File

@ -1,5 +1,5 @@
import { IssueEmojiReactions, IssueVotes } from "@/components/issues/peek-overview"; 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 = () => { export const IssueReactions: React.FC = () => {
const { project: projectStore } = useMobxStore(); const { project: projectStore } = useMobxStore();

View File

@ -1,15 +1,9 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// mobx
// lib
import { Tooltip } from "@plane/ui"; import { Tooltip } from "@plane/ui";
// hooks // hooks
import { useUser } from "@/hooks/store"; import { useMobxStore, useUser } from "@/hooks/store";
import { useMobxStore } from "@/lib/mobx/store-provider";
// ui
export const IssueVotes: React.FC = observer(() => { export const IssueVotes: React.FC = observer(() => {
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);

View File

@ -9,7 +9,7 @@ import { Dialog, Transition } from "@headlessui/react";
// components // components
import { FullScreenPeekView, SidePeekView } from "@/components/issues/peek-overview"; import { FullScreenPeekView, SidePeekView } from "@/components/issues/peek-overview";
// lib // lib
import { useMobxStore } from "@/lib/mobx/store-provider"; import { useMobxStore } from "@/hooks/store";
export const IssuePeekOverview: React.FC = observer(() => { export const IssuePeekOverview: React.FC = observer(() => {
// states // states

View File

@ -20,14 +20,14 @@ export const AuthView = observer(() => {
const { data: currentUser, fetchCurrentUser, isLoading } = useUser(); const { data: currentUser, fetchCurrentUser, isLoading } = useUser();
// fetching user information // fetching user information
useSWR("CURRENT_USER_DETAILS", () => fetchCurrentUser(), { const { isLoading: isSWRLoading } = useSWR("CURRENT_USER_DETAILS", () => fetchCurrentUser(), {
shouldRetryOnError: false, shouldRetryOnError: false,
revalidateOnFocus: false, revalidateOnFocus: false,
}); });
return ( return (
<> <>
{isLoading ? ( {isLoading || isSWRLoading ? (
<div className="relative flex h-screen w-screen items-center justify-center"> <div className="relative flex h-screen w-screen items-center justify-center">
<Spinner /> <Spinner />
</div> </div>

View File

@ -11,8 +11,7 @@ import { IssueSpreadsheetView } from "@/components/issues/board-views/spreadshee
import { IssueAppliedFilters } from "@/components/issues/filters/applied-filters/root"; import { IssueAppliedFilters } from "@/components/issues/filters/applied-filters/root";
import { IssuePeekOverview } from "@/components/issues/peek-overview"; import { IssuePeekOverview } from "@/components/issues/peek-overview";
// mobx store // mobx store
import { useUser } from "@/hooks/store"; import { useMobxStore, useUser } from "@/hooks/store";
import { useMobxStore } from "@/lib/mobx/store-provider";
import { RootStore } from "@/store/root.store"; import { RootStore } from "@/store/root.store";
// assets // assets
import SomethingWentWrongImage from "public/something-went-wrong.svg"; import SomethingWentWrongImage from "public/something-went-wrong.svg";

View File

@ -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: (
<div>
Your account is already registered.&nbsp;
<Link
className="underline underline-offset-4 font-medium hover:font-bold transition-all"
href={`/accounts/sign-in`}
>
Sign In
</Link>
&nbsp;now.
</div>
),
},
[EAuthenticationErrorCodes.USER_DOES_NOT_EXIST]: {
title: `User does not exist`,
message: (
<div>
No account found.&nbsp;
<Link className="underline underline-offset-4 font-medium hover:font-bold transition-all" href={`/`}>
Create one
</Link>
&nbsp;to get started.
</div>
),
},
[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;
};

View File

@ -1,2 +1,4 @@
export * from "./user-mobx-provider";
export * from "./use-instance"; export * from "./use-instance";
export * from "./user"; export * from "./user";

View File

@ -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;
};

View File

@ -1,4 +1,4 @@
import { useMobxStore } from "@/lib/mobx/store-provider"; import { useMobxStore } from "@/hooks/store";
import { RootStore } from "@/store/root.store"; import { RootStore } from "@/store/root.store";
const useEditorSuggestions = () => { const useEditorSuggestions = () => {

View File

@ -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>(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 <MobxStoreContext.Provider value={store}>{children}</MobxStoreContext.Provider>;
};
// hook
export const useMobxStore = () => {
const context = useContext(MobxStoreContext);
if (context === undefined) throw new Error("useMobxStore must be used within MobxStoreProvider");
return context;
};

View File

@ -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<TAuthWrapper> = 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 (
<div className="relative flex h-screen w-full items-center justify-center">
<Spinner />
</div>
);
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}</>;
});

View File

@ -0,0 +1,2 @@
export * from "./instance-wrapper";
export * from "./auth-wrapper";

View File

@ -8,20 +8,20 @@ import { InstanceNotReady } from "@/components/instance";
// hooks // hooks
import { useInstance } from "@/hooks/store"; import { useInstance } from "@/hooks/store";
type TInstanceLayout = { type TInstanceWrapper = {
children: ReactNode; children: ReactNode;
}; };
const InstanceLayout: FC<TInstanceLayout> = observer((props) => { export const InstanceWrapper: FC<TInstanceWrapper> = observer((props) => {
const { children } = props; const { children } = props;
// store // hooks
const { isLoading, instance, fetchInstanceInfo } = useInstance(); const { isLoading, instance, fetchInstanceInfo } = useInstance();
useSWR("INSTANCE_INFORMATION", () => fetchInstanceInfo(), { const { isLoading: isSWRLoading } = useSWR("INSTANCE_INFORMATION", () => fetchInstanceInfo(), {
revalidateOnFocus: false, revalidateOnFocus: false,
}); });
if (isLoading) if (isSWRLoading || isLoading)
return ( return (
<div className="relative flex h-screen w-full items-center justify-center"> <div className="relative flex h-screen w-full items-center justify-center">
<Spinner /> <Spinner />
@ -32,5 +32,3 @@ const InstanceLayout: FC<TInstanceLayout> = observer((props) => {
return <>{children}</>; return <>{children}</>;
}); });
export default InstanceLayout;

View File

@ -1,14 +1,16 @@
import Head from "next/head"; import Head from "next/head";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import useSWR from "swr"; import useSWR from "swr";
/// layouts
// components // components
import { ProjectDetailsView } from "@/components/views"; import { ProjectDetailsView } from "@/components/views";
// lib // helpers
import { useMobxStore } from "@/lib/mobx/store-provider"; import { EPageTypes } from "@/helpers/authentication.helper";
import ProjectLayout from "layouts/project-layout"; // hooks
import { useMobxStore } from "@/hooks/store";
// layouts
import ProjectLayout from "@/layouts/project-layout";
// wrappers
import { AuthWrapper } from "@/lib/wrappers";
const WorkspaceProjectPage = (props: any) => { const WorkspaceProjectPage = (props: any) => {
const SITE_TITLE = props?.project_settings?.project_details?.name || "Plane | Deploy"; const SITE_TITLE = props?.project_settings?.project_details?.name || "Plane | Deploy";
@ -31,12 +33,14 @@ const WorkspaceProjectPage = (props: any) => {
}); });
return ( return (
<ProjectLayout> <AuthWrapper pageType={EPageTypes.AUTHENTICATED}>
<Head> <ProjectLayout>
<title>{SITE_TITLE}</title> <Head>
</Head> <title>{SITE_TITLE}</title>
<ProjectDetailsView /> </Head>
</ProjectLayout> <ProjectDetailsView />
</ProjectLayout>
</AuthWrapper>
); );
}; };

View File

@ -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 { SITE_NAME, SITE_DESCRIPTION, SITE_URL, TWITTER_USER_NAME, SITE_KEYWORDS, SITE_TITLE } from "@/constants/seo";
import { ToastContextProvider } from "@/contexts/toast.context"; import { ToastContextProvider } from "@/contexts/toast.context";
// mobx store provider // mobx store provider
import InstanceLayout from "@/layouts/instance-layout"; import { StoreProvider } from "@/lib/store-context";
import { MobxStoreProvider } from "@/lib/mobx/store-provider"; // wrappers
// constants import { InstanceWrapper } from "@/lib/wrappers";
const prefix = parseInt(process.env.NEXT_PUBLIC_DEPLOY_WITH_NGINX || "0") === 0 ? "/" : "/spaces/"; const prefix = parseInt(process.env.NEXT_PUBLIC_DEPLOY_WITH_NGINX || "0") === 0 ? "/" : "/spaces/";
function MyApp({ Component, pageProps }: AppProps) { function MyApp({ Component, pageProps }: AppProps) {
return ( return (
<MobxStoreProvider> <>
<Head> <Head>
<title>{SITE_TITLE}</title> <title>{SITE_TITLE}</title>
<meta property="og:site_name" content={SITE_NAME} /> <meta property="og:site_name" content={SITE_NAME} />
@ -31,14 +31,16 @@ function MyApp({ Component, pageProps }: AppProps) {
<link rel="manifest" href={`${prefix}site.webmanifest.json`} /> <link rel="manifest" href={`${prefix}site.webmanifest.json`} />
<link rel="shortcut icon" href={`${prefix}favicon/favicon.ico`} /> <link rel="shortcut icon" href={`${prefix}favicon/favicon.ico`} />
</Head> </Head>
<ToastContextProvider> <StoreProvider>
<ThemeProvider themes={["light", "dark"]} defaultTheme="system" enableSystem> <ToastContextProvider>
<InstanceLayout> <ThemeProvider themes={["light", "dark"]} defaultTheme="system" enableSystem>
<Component {...pageProps} /> <InstanceWrapper>
</InstanceLayout> <Component {...pageProps} />
</ThemeProvider> </InstanceWrapper>
</ToastContextProvider> </ThemeProvider>
</MobxStoreProvider> </ToastContextProvider>
</StoreProvider>
</>
); );
} }

View File

@ -9,11 +9,13 @@ import { CircleCheck } from "lucide-react";
// ui // ui
import { Button, Input, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui"; import { Button, Input, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui";
// helpers // helpers
import { EPageTypes } from "@/helpers/authentication.helper";
import { cn } from "@/helpers/common.helper"; import { cn } from "@/helpers/common.helper";
import { checkEmailValidity } from "@/helpers/string.helper"; import { checkEmailValidity } from "@/helpers/string.helper";
// hooks // hooks
// import useAuthRedirection from "@/hooks/use-auth-redirection";
import useTimer from "@/hooks/use-timer"; import useTimer from "@/hooks/use-timer";
// wrappers
import { AuthWrapper } from "@/lib/wrappers";
// services // services
import { AuthService } from "@/services/authentication.service"; import { AuthService } from "@/services/authentication.service";
// images // images
@ -77,85 +79,87 @@ const ForgotPasswordPage: NextPage = () => {
}; };
return ( return (
<div className="relative h-screen w-full overflow-hidden"> <AuthWrapper pageType={EPageTypes.NON_AUTHENTICATED}>
<div className="absolute inset-0 z-0"> <div className="relative h-screen w-full overflow-hidden">
<Image <div className="absolute inset-0 z-0">
src={resolvedTheme === "dark" ? PlaneBackgroundPatternDark : PlaneBackgroundPattern} <Image
className="w-full h-full object-cover" src={resolvedTheme === "dark" ? PlaneBackgroundPatternDark : PlaneBackgroundPattern}
alt="Plane background pattern" className="w-full h-full object-cover"
/> alt="Plane background pattern"
</div> />
<div className="relative z-10">
<div className="flex items-center justify-between px-8 pb-4 sm:px-16 sm:py-5 lg:px-28">
<div className="flex items-center gap-x-2 py-10">
<Image src={BluePlaneLogoWithoutText} height={30} width={30} alt="Plane Logo" className="mr-2" />
<span className="text-2xl font-semibold sm:text-3xl">Plane</span>
</div>
</div> </div>
<div className="mx-auto h-full"> <div className="relative z-10">
<div className="h-full overflow-auto px-7 pb-56 pt-4 sm:px-0"> <div className="flex items-center justify-between px-8 pb-4 sm:px-16 sm:py-5 lg:px-28">
<div className="mx-auto flex flex-col"> <div className="flex items-center gap-x-2 py-10">
<div className="text-center space-y-1 py-4 mx-auto sm:w-96"> <Image src={BluePlaneLogoWithoutText} height={30} width={30} alt="Plane Logo" className="mr-2" />
<h3 className="flex gap-4 justify-center text-3xl font-bold text-onboarding-text-100"> <span className="text-2xl font-semibold sm:text-3xl">Plane</span>
Reset your password </div>
</h3> </div>
<p className="font-medium text-onboarding-text-400"> <div className="mx-auto h-full">
Enter your user account{"'"}s verified email address and we will send you a password reset link. <div className="h-full overflow-auto px-7 pb-56 pt-4 sm:px-0">
</p> <div className="mx-auto flex flex-col">
</div> <div className="text-center space-y-1 py-4 mx-auto sm:w-96">
<form onSubmit={handleSubmit(handleForgotPassword)} className="mx-auto mt-5 space-y-4 w-5/6 sm:w-96"> <h3 className="flex gap-4 justify-center text-3xl font-bold text-onboarding-text-100">
<div className="space-y-1"> Reset your password
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="email"> </h3>
Email <p className="font-medium text-onboarding-text-400">
</label> Enter your user account{"'"}s verified email address and we will send you a password reset link.
<Controller </p>
control={control}
name="email"
rules={{
required: "Email is required",
validate: (value) => checkEmailValidity(value) || "Email is invalid",
}}
render={({ field: { value, onChange, ref } }) => (
<Input
id="email"
name="email"
type="email"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.email)}
placeholder="name@company.com"
className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
disabled={resendTimerCode > 0}
/>
)}
/>
{resendTimerCode > 0 && (
<p className="flex w-full items-start px-1 gap-1 text-xs font-medium text-green-700">
<CircleCheck height={12} width={12} className="mt-0.5" />
We sent the reset link to your email address
</p>
)}
</div> </div>
<Button <form onSubmit={handleSubmit(handleForgotPassword)} className="mx-auto mt-5 space-y-4 w-5/6 sm:w-96">
type="submit" <div className="space-y-1">
variant="primary" <label className="text-sm text-onboarding-text-300 font-medium" htmlFor="email">
className="w-full" Email
size="lg" </label>
disabled={!isValid} <Controller
loading={isSubmitting || resendTimerCode > 0} control={control}
> name="email"
{resendTimerCode > 0 ? `Resend in ${resendTimerCode} seconds` : "Send reset link"} rules={{
</Button> required: "Email is required",
<Link href="/" className={cn("w-full", getButtonStyling("link-neutral", "lg"))}> validate: (value) => checkEmailValidity(value) || "Email is invalid",
Back to sign in }}
</Link> render={({ field: { value, onChange, ref } }) => (
</form> <Input
id="email"
name="email"
type="email"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.email)}
placeholder="name@company.com"
className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
disabled={resendTimerCode > 0}
/>
)}
/>
{resendTimerCode > 0 && (
<p className="flex w-full items-start px-1 gap-1 text-xs font-medium text-green-700">
<CircleCheck height={12} width={12} className="mt-0.5" />
We sent the reset link to your email address
</p>
)}
</div>
<Button
type="submit"
variant="primary"
className="w-full"
size="lg"
disabled={!isValid}
loading={isSubmitting || resendTimerCode > 0}
>
{resendTimerCode > 0 ? `Resend in ${resendTimerCode} seconds` : "Send reset link"}
</Button>
<Link href="/" className={cn("w-full", getButtonStyling("link-neutral", "lg"))}>
Back to sign in
</Link>
</form>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </AuthWrapper>
); );
}; };

View File

@ -10,9 +10,11 @@ import { Button, Input } from "@plane/ui";
// components // components
import { PasswordStrengthMeter } from "@/components/accounts"; import { PasswordStrengthMeter } from "@/components/accounts";
// helpers // helpers
import { EPageTypes } from "@/helpers/authentication.helper";
import { API_BASE_URL } from "@/helpers/common.helper"; import { API_BASE_URL } from "@/helpers/common.helper";
import { getPasswordStrength } from "@/helpers/password.helper"; import { getPasswordStrength } from "@/helpers/password.helper";
// hooks // wrappers
import { AuthWrapper } from "@/lib/wrappers";
// services // services
import { AuthService } from "@/services/authentication.service"; import { AuthService } from "@/services/authentication.service";
// images // images
@ -75,98 +77,71 @@ const ResetPasswordPage: NextPage = () => {
); );
return ( return (
<div className="relative h-screen w-full overflow-hidden"> <AuthWrapper pageType={EPageTypes.NON_AUTHENTICATED}>
<div className="absolute inset-0 z-0"> <div className="relative h-screen w-full overflow-hidden">
<Image <div className="absolute inset-0 z-0">
src={resolvedTheme === "dark" ? PlaneBackgroundPatternDark : PlaneBackgroundPattern} <Image
className="w-full h-full object-cover" src={resolvedTheme === "dark" ? PlaneBackgroundPatternDark : PlaneBackgroundPattern}
alt="Plane background pattern" className="w-full h-full object-cover"
/> alt="Plane background pattern"
</div> />
<div className="relative z-10">
<div className="flex items-center justify-between px-8 pb-4 sm:px-16 sm:py-5 lg:px-28">
<div className="flex items-center gap-x-2 py-10">
<Image src={BluePlaneLogoWithoutText} height={30} width={30} alt="Plane Logo" className="mr-2" />
<span className="text-2xl font-semibold sm:text-3xl">Plane</span>
</div>
</div> </div>
<div className="mx-auto h-full"> <div className="relative z-10">
<div className="h-full overflow-auto px-7 pb-56 pt-4 sm:px-0"> <div className="flex items-center justify-between px-8 pb-4 sm:px-16 sm:py-5 lg:px-28">
<div className="mx-auto flex flex-col"> <div className="flex items-center gap-x-2 py-10">
<div className="text-center space-y-1 py-4 mx-auto sm:w-96"> <Image src={BluePlaneLogoWithoutText} height={30} width={30} alt="Plane Logo" className="mr-2" />
<h3 className="flex gap-4 justify-center text-3xl font-bold text-onboarding-text-100"> <span className="text-2xl font-semibold sm:text-3xl">Plane</span>
Set new password </div>
</h3> </div>
<p className="font-medium text-onboarding-text-400">Secure your account with a strong password</p> <div className="mx-auto h-full">
</div> <div className="h-full overflow-auto px-7 pb-56 pt-4 sm:px-0">
<form <div className="mx-auto flex flex-col">
className="mx-auto mt-5 space-y-4 w-5/6 sm:w-96" <div className="text-center space-y-1 py-4 mx-auto sm:w-96">
method="POST" <h3 className="flex gap-4 justify-center text-3xl font-bold text-onboarding-text-100">
action={`${API_BASE_URL}/auth/reset-password/${uidb64?.toString()}/${token?.toString()}/`} Set new password
> </h3>
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} /> <p className="font-medium text-onboarding-text-400">Secure your account with a strong password</p>
<div className="space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="email">
Email
</label>
<div className="relative flex items-center rounded-md bg-onboarding-background-200">
<Input
id="email"
name="email"
type="email"
value={resetFormData.email}
//hasError={Boolean(errors.email)}
placeholder="name@company.com"
className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 text-onboarding-text-400 cursor-not-allowed"
disabled
/>
</div>
</div> </div>
<div className="space-y-1"> <form
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="password"> className="mx-auto mt-5 space-y-4 w-5/6 sm:w-96"
Password method="POST"
</label> action={`${API_BASE_URL}/auth/reset-password/${uidb64?.toString()}/${token?.toString()}/`}
<div className="relative flex items-center rounded-md bg-onboarding-background-200"> >
<Input <input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
type={showPassword ? "text" : "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 ? (
<EyeOff
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
onClick={() => setShowPassword(false)}
/>
) : (
<Eye
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
onClick={() => setShowPassword(true)}
/>
)}
</div>
{isPasswordInputFocused && <PasswordStrengthMeter password={resetFormData.password} />}
</div>
{getPasswordStrength(resetFormData.password) >= 3 && (
<div className="space-y-1"> <div className="space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="confirm_password"> <label className="text-sm text-onboarding-text-300 font-medium" htmlFor="email">
Confirm password Email
</label>
<div className="relative flex items-center rounded-md bg-onboarding-background-200">
<Input
id="email"
name="email"
type="email"
value={resetFormData.email}
//hasError={Boolean(errors.email)}
placeholder="name@company.com"
className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 text-onboarding-text-400 cursor-not-allowed"
disabled
/>
</div>
</div>
<div className="space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="password">
Password
</label> </label>
<div className="relative flex items-center rounded-md bg-onboarding-background-200"> <div className="relative flex items-center rounded-md bg-onboarding-background-200">
<Input <Input
type={showPassword ? "text" : "password"} type={showPassword ? "text" : "password"}
name="confirm_password" name="password"
value={resetFormData.confirm_password} value={resetFormData.password}
onChange={(e) => handleFormChange("confirm_password", e.target.value)} onChange={(e) => handleFormChange("password", e.target.value)}
placeholder="Confirm password" //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" 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 ? ( {showPassword ? (
<EyeOff <EyeOff
@ -180,20 +155,50 @@ const ResetPasswordPage: NextPage = () => {
/> />
)} )}
</div> </div>
{!!resetFormData.confirm_password && resetFormData.password !== resetFormData.confirm_password && ( {isPasswordInputFocused && <PasswordStrengthMeter password={resetFormData.password} />}
<span className="text-sm text-red-500">Passwords don{"'"}t match</span>
)}
</div> </div>
)} {getPasswordStrength(resetFormData.password) >= 3 && (
<Button type="submit" variant="primary" className="w-full" size="lg" disabled={isButtonDisabled}> <div className="space-y-1">
Set password <label className="text-sm text-onboarding-text-300 font-medium" htmlFor="confirm_password">
</Button> Confirm password
</form> </label>
<div className="relative flex items-center rounded-md bg-onboarding-background-200">
<Input
type={showPassword ? "text" : "password"}
name="confirm_password"
value={resetFormData.confirm_password}
onChange={(e) => 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 ? (
<EyeOff
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
onClick={() => setShowPassword(false)}
/>
) : (
<Eye
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
onClick={() => setShowPassword(true)}
/>
)}
</div>
{!!resetFormData.confirm_password &&
resetFormData.password !== resetFormData.confirm_password && (
<span className="text-sm text-red-500">Passwords don{"'"}t match</span>
)}
</div>
)}
<Button type="submit" variant="primary" className="w-full" size="lg" disabled={isButtonDisabled}>
Set password
</Button>
</form>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </AuthWrapper>
); );
}; };

View File

@ -2,8 +2,15 @@ import { observer } from "mobx-react-lite";
import { NextPage } from "next"; import { NextPage } from "next";
// components // components
import { AuthView } from "@/components/views"; import { AuthView } from "@/components/views";
// store // helpers
import { EPageTypes } from "@/helpers/authentication.helper";
// wrapper
import { AuthWrapper } from "@/lib/wrappers";
const Index: NextPage = observer(() => <AuthView />); const Index: NextPage = observer(() => (
<AuthWrapper pageType={EPageTypes.INIT}>
<AuthView />
</AuthWrapper>
));
export default Index; export default Index;

View File

@ -7,8 +7,12 @@ import { useTheme } from "next-themes";
import { Avatar } from "@plane/ui"; import { Avatar } from "@plane/ui";
// components // components
import { OnBoardingForm } from "@/components/accounts/onboarding-form"; import { OnBoardingForm } from "@/components/accounts/onboarding-form";
// mobx // helpers
import { EPageTypes } from "@/helpers/authentication.helper";
// hooks
import { useUser, useUserProfile } from "@/hooks/store"; import { useUser, useUserProfile } from "@/hooks/store";
// wrappers
import { AuthWrapper } from "@/lib/wrappers";
// assets // assets
import ProfileSetupDark from "public/onboarding/profile-setup-dark.svg"; import ProfileSetupDark from "public/onboarding/profile-setup-dark.svg";
import ProfileSetup from "public/onboarding/profile-setup.svg"; import ProfileSetup from "public/onboarding/profile-setup.svg";
@ -49,71 +53,78 @@ const OnBoardingPage = observer(() => {
}; };
return ( return (
<div className="flex h-full w-full"> <AuthWrapper pageType={EPageTypes.ONBOARDING}>
<div className="w-full h-full overflow-auto px-6 py-10 sm:px-7 sm:py-14 md:px-14 lg:px-28"> <div className="flex h-full w-full">
<div className="flex items-center justify-between"> <div className="w-full h-full overflow-auto px-6 py-10 sm:px-7 sm:py-14 md:px-14 lg:px-28">
<div className="flex w-full items-center justify-between font-semibold "> <div className="flex items-center justify-between">
<div className="flex items-center gap-x-2"> <div className="flex w-full items-center justify-between font-semibold ">
<Image src={`${imagePrefix}/plane-logos/blue-without-text.png`} height={30} width={30} alt="Plane Logo" /> <div className="flex items-center gap-x-2">
<Image
src={`${imagePrefix}/plane-logos/blue-without-text.png`}
height={30}
width={30}
alt="Plane Logo"
/>
</div>
</div> </div>
</div> <div className="shrink-0 lg:hidden">
<div className="shrink-0 lg:hidden"> <div className="flex w-full shrink-0 justify-end">
<div className="flex w-full shrink-0 justify-end"> <div className="flex items-center gap-x-2 pr-4">
<div className="flex items-center gap-x-2 pr-4"> {user?.avatar && (
{user?.avatar && ( <Avatar
<Avatar name={user?.first_name ? `${user?.first_name} ${user?.last_name ?? ""}` : user?.email}
name={user?.first_name ? `${user?.first_name} ${user?.last_name ?? ""}` : user?.email} src={user?.avatar}
src={user?.avatar} size={24}
size={24} shape="square"
shape="square" fallbackBackgroundColor="#FCBE1D"
fallbackBackgroundColor="#FCBE1D" className="!text-base capitalize"
className="!text-base capitalize" />
/> )}
)} <span className="text-sm font-medium text-custom-text-200">
<span className="text-sm font-medium text-custom-text-200"> {user?.first_name ? `${user?.first_name} ${user?.last_name ?? ""}` : user?.email}
{user?.first_name ? `${user?.first_name} ${user?.last_name ?? ""}` : user?.email} </span>
</span> </div>
</div> </div>
</div> </div>
</div> </div>
</div> <div className="flex flex-col w-full items-center justify-center p-8 mt-14">
<div className="flex flex-col w-full items-center justify-center p-8 mt-14"> <div className="text-center space-y-1 py-4 mx-auto">
<div className="text-center space-y-1 py-4 mx-auto"> <h3 className="text-3xl font-bold text-onboarding-text-100">Welcome to Plane!</h3>
<h3 className="text-3xl font-bold text-onboarding-text-100">Welcome to Plane!</h3> <p className="font-medium text-onboarding-text-400">
<p className="font-medium text-onboarding-text-400"> Lets setup your profile, tell us a bit about yourself.
Lets setup your profile, tell us a bit about yourself. </p>
</p> </div>
</div> <OnBoardingForm user={user} finishOnboarding={finishOnboarding} />
<OnBoardingForm user={user} finishOnboarding={finishOnboarding} />
</div>
</div>
<div className="hidden lg:block relative w-2/5 h-screen overflow-hidden px-6 py-10 sm:px-7 sm:py-14 md:px-14 lg:px-28">
<div className="flex w-full shrink-0 justify-end">
<div className="flex items-center gap-x-2 pr-4 z-10">
{user?.avatar && (
<Avatar
name={user?.first_name ? `${user?.first_name} ${user?.last_name ?? ""}` : user?.email}
src={user?.avatar}
size={24}
shape="square"
fallbackBackgroundColor="#FCBE1D"
className="!text-base capitalize"
/>
)}
<span className="text-sm font-medium text-custom-text-200">
{user?.first_name ? `${user?.first_name} ${user?.last_name ?? ""}` : user?.email}
</span>
</div> </div>
</div> </div>
<div className="absolute inset-0 z-0"> <div className="hidden lg:block relative w-2/5 h-screen overflow-hidden px-6 py-10 sm:px-7 sm:py-14 md:px-14 lg:px-28">
<Image <div className="flex w-full shrink-0 justify-end">
src={resolvedTheme === "dark" ? ProfileSetupDark : ProfileSetup} <div className="flex items-center gap-x-2 pr-4 z-10">
className="h-screen w-auto float-end object-cover" {user?.avatar && (
alt="Profile setup" <Avatar
/> name={user?.first_name ? `${user?.first_name} ${user?.last_name ?? ""}` : user?.email}
src={user?.avatar}
size={24}
shape="square"
fallbackBackgroundColor="#FCBE1D"
className="!text-base capitalize"
/>
)}
<span className="text-sm font-medium text-custom-text-200">
{user?.first_name ? `${user?.first_name} ${user?.last_name ?? ""}` : user?.email}
</span>
</div>
</div>
<div className="absolute inset-0 z-0">
<Image
src={resolvedTheme === "dark" ? ProfileSetupDark : ProfileSetup}
className="h-screen w-auto float-end object-cover"
alt="Profile setup"
/>
</div>
</div> </div>
</div> </div>
</div> </AuthWrapper>
); );
}); });

View File

@ -1,32 +1,39 @@
// next imports // next imports
import Image from "next/image"; 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 = () => ( const CustomProjectNotPublishedError = () => (
<div className="relative flex h-full min-h-screen w-screen items-center justify-center py-5"> <AuthWrapper pageType={EPageTypes.PUBLIC}>
<div className="max-w-[700px] space-y-5"> <div className="relative flex h-full min-h-screen w-screen items-center justify-center py-5">
<div className="flex flex-col items-center gap-3 text-center"> <div className="max-w-[700px] space-y-5">
<div className="relative h-[240px] w-[240px]"> <div className="flex flex-col items-center gap-3 text-center">
<Image src={projectNotPublishedImage} layout="fill" alt="404- Page not found" /> <div className="relative h-[240px] w-[240px]">
<Image src={projectNotPublishedImage} layout="fill" alt="404- Page not found" />
</div>
<div className="text-xl font-medium">
Oops! The page you{`'`}re looking for isn{`'`}t live at the moment.
</div>
<div className="text-sm text-custom-text-200">
If this is your project, login to your workspace to adjust its visibility settings and make it public.
</div>
</div> </div>
<div className="text-xl font-medium">
Oops! The page you{`'`}re looking for isn{`'`}t live at the moment.
</div>
<div className="text-sm text-custom-text-200">
If this is your project, login to your workspace to adjust its visibility settings and make it public.
</div>
</div>
<div className="flex items-center justify-center text-center"> <div className="flex items-center justify-center text-center">
<a <a
href={`https://app.plane.so/`} href={`https://app.plane.so/`}
className="cursor-pointer select-none rounded-sm border border-gray-200 bg-gray-50 p-1.5 px-2.5 text-sm font-medium text-gray-700 transition-all hover:scale-105 hover:bg-gray-100 hover:text-gray-800" className="cursor-pointer select-none rounded-sm border border-gray-200 bg-gray-50 p-1.5 px-2.5 text-sm font-medium text-gray-700 transition-all hover:scale-105 hover:bg-gray-100 hover:text-gray-800"
> >
Go to your Workspace Go to your Workspace
</a> </a>
</div>
</div> </div>
</div> </div>
</div> </AuthWrapper>
); );
export default CustomProjectNotPublishedError; export default CustomProjectNotPublishedError;

View File

@ -19,7 +19,7 @@ abstract class APIService {
this.axiosInstance.interceptors.response.use( this.axiosInstance.interceptors.response.use(
(response) => response, (response) => response,
(error) => { (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); return Promise.reject(error.response?.data ?? error);
} }
); );

View File

@ -26,7 +26,7 @@ export class AuthService extends APIService {
}); });
} }
async sendResetPasswordLink(data: { email: string }): Promise<any> { async sendResetPasswordLink(data: { email: string }): Promise<void> {
return this.post(`/auth/forgot-password/`, data) return this.post(`/auth/forgot-password/`, data)
.then((response) => response?.data) .then((response) => response?.data)
.catch((error) => { .catch((error) => {
@ -34,7 +34,7 @@ export class AuthService extends APIService {
}); });
} }
async generateUniqueCode(data: { email: string }): Promise<any> { async generateUniqueCode(data: { email: string }): Promise<void> {
return this.post("/auth/spaces/magic-generate/", data, { headers: {} }) return this.post("/auth/spaces/magic-generate/", data, { headers: {} })
.then((response) => response?.data) .then((response) => response?.data)
.catch((error) => { .catch((error) => {

View File

@ -18,7 +18,7 @@ export class UserService extends APIService {
}); });
} }
async updateUser(data: Partial<IUser>): Promise<any> { async updateUser(data: Partial<IUser>): Promise<IUser> {
return this.patch("/api/users/me/", data) return this.patch("/api/users/me/", data)
.then((response) => response?.data) .then((response) => response?.data)
.catch((error) => { .catch((error) => {
@ -33,7 +33,7 @@ export class UserService extends APIService {
throw error?.response; throw error?.response;
}); });
} }
async updateCurrentUserProfile(data: any): Promise<any> { async updateCurrentUserProfile(data: Partial<TUserProfile>): Promise<TUserProfile> {
return this.patch("/api/users/me/profile/", data) return this.patch("/api/users/me/profile/", data)
.then((response) => response?.data) .then((response) => response?.data)
.catch((error) => { .catch((error) => {

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react"; import React, { FC, useEffect, useState } from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { IEmailCheckData } from "@plane/types"; import { IEmailCheckData } from "@plane/types";
@ -29,26 +29,42 @@ import { AuthService } from "@/services/auth.service";
const authService = new AuthService(); const authService = new AuthService();
export const SignInAuthRoot = observer(() => { type TAuthRoot = {
authMode: EAuthModes;
};
export const AuthRoot: FC<TAuthRoot> = observer((props) => {
//router //router
const router = useRouter(); 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 // states
const [authStep, setAuthStep] = useState<EAuthSteps>(EAuthSteps.EMAIL); const [authStep, setAuthStep] = useState<EAuthSteps>(EAuthSteps.EMAIL);
const [email, setEmail] = useState(emailParam ? emailParam.toString() : ""); const [email, setEmail] = useState(emailParam ? emailParam.toString() : "");
const [errorInfo, setErrorInfo] = useState<TAuthErrorInfo | undefined>(undefined); const [errorInfo, setErrorInfo] = useState<TAuthErrorInfo | undefined>(undefined);
// hooks // hooks
const { instance } = useInstance(); const { instance } = useInstance();
// derived values
const authMode = EAuthModes.SIGN_IN;
useEffect(() => { useEffect(() => {
if (error_code && error_message) { if (error_code) {
const errorhandler = authErrorHandler( const errorhandler = authErrorHandler(error_code?.toString() as EAuthenticationErrorCodes);
error_code?.toString() as EAuthenticationErrorCodes,
error_message?.toString()
);
if (errorhandler) { 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) { if (errorhandler?.type === EErrorAlertType.TOAST_ALERT) {
setToast({ setToast({
type: TOAST_TYPE.ERROR, type: TOAST_TYPE.ERROR,
@ -58,23 +74,30 @@ export const SignInAuthRoot = observer(() => {
} else setErrorInfo(errorhandler); } else setErrorInfo(errorhandler);
} }
} }
}, [error_code, error_message]); }, [error_code, authMode]);
// step 1 submit handler- email verification // step 1 submit handler- email verification
const handleEmailVerification = async (data: IEmailCheckData) => { const handleEmailVerification = async (data: IEmailCheckData) => {
setEmail(data.email); setEmail(data.email);
await authService const emailCheckRequest =
.signInEmailCheck(data) authMode === EAuthModes.SIGN_IN ? authService.signInEmailCheck(data) : authService.signUpEmailCheck(data);
await emailCheckRequest
.then(() => { .then(() => {
setAuthStep(EAuthSteps.PASSWORD); setAuthStep(EAuthSteps.PASSWORD);
}) })
.catch((err) => { .catch((error) => {
setToast({ const errorhandler = authErrorHandler(error?.error_code.toString());
type: TOAST_TYPE.ERROR, if (errorhandler?.type === EErrorAlertType.BANNER_ALERT) {
title: "Error!", setErrorInfo(errorhandler);
message: err?.error_message ?? "Something went wrong. Please try again.", 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} workspaceSlug={workspaceSlug?.toString() || undefined}
invitationId={invitation_id?.toString() || undefined} invitationId={invitation_id?.toString() || undefined}
invitationEmail={email || undefined} invitationEmail={email || undefined}
authMode={EAuthModes.SIGN_IN} authMode={authMode}
currentAuthStep={authStep} currentAuthStep={authStep}
> >
{errorInfo && errorInfo?.type === EErrorAlertType.BANNER_ALERT && ( {errorInfo && errorInfo?.type === EErrorAlertType.BANNER_ALERT && (

View File

@ -1,5 +1,4 @@
export * from "./sign-up-root"; export * from "./auth-root";
export * from "./sign-in-root";
export * from "./auth-header"; export * from "./auth-header";
export * from "./auth-banner"; export * from "./auth-banner";

View File

@ -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>(EAuthSteps.EMAIL);
const [email, setEmail] = useState(emailParam ? emailParam.toString() : "");
const [errorInfo, setErrorInfo] = useState<TAuthErrorInfo | undefined>(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 (
<div className="relative flex flex-col space-y-6">
<AuthHeader
workspaceSlug={workspaceSlug?.toString() || undefined}
invitationId={invitation_id?.toString() || undefined}
invitationEmail={email || undefined}
authMode={EAuthModes.SIGN_UP}
currentAuthStep={authStep}
>
{errorInfo && errorInfo?.type === EErrorAlertType.BANNER_ALERT && (
<AuthBanner bannerData={errorInfo} handleBannerData={(value) => setErrorInfo(value)} />
)}
{authStep === EAuthSteps.EMAIL && <AuthEmailForm defaultEmail={email} onSubmit={handleEmailVerification} />}
{authStep === EAuthSteps.UNIQUE_CODE && (
<AuthUniqueCodeForm
email={email}
handleEmailClear={() => {
setEmail("");
setAuthStep(EAuthSteps.EMAIL);
}}
submitButtonText="Continue"
mode={authMode}
/>
)}
{authStep === EAuthSteps.PASSWORD && (
<AuthPasswordForm
email={email}
handleEmailClear={() => {
setEmail("");
setAuthStep(EAuthSteps.EMAIL);
}}
handleStepChange={(step) => setAuthStep(step)}
mode={authMode}
/>
)}
{isOAuthEnabled && <OAuthOptions />}
<TermsAndConditions isSignUp={authMode === EAuthModes.SIGN_UP} />
</AuthHeader>
</div>
);
});

View File

@ -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;
};

View File

@ -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: (
<div>
Your account is already registered.&nbsp;
<Link
className="underline underline-offset-4 font-medium hover:font-bold transition-all"
href={`/accounts/sign-in`}
>
Sign In
</Link>
&nbsp;now.
</div>
),
},
[EAuthenticationErrorCodes.USER_DOES_NOT_EXIST]: {
title: `User does not exist`,
message: (
<div>
No account found.&nbsp;
<Link className="underline underline-offset-4 font-medium hover:font-bold transition-all" href={`/`}>
Create one
</Link>
&nbsp;to get started.
</div>
),
},
[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;
};

View File

@ -30,12 +30,7 @@ export const InstanceWrapper: FC<TInstanceWrapper> = observer((props) => {
); );
// something went wrong while in the request // something went wrong while in the request
if (error && error?.status === "error") if (error && error?.status === "error") return <>{children}</>;
return (
<div className="relative flex h-screen w-screen items-center justify-center">
Something went wrong. please try again later
</div>
);
// instance is not ready and setup is not done // instance is not ready and setup is not done
if (instance?.instance?.is_setup_done === false) return <InstanceNotReady />; if (instance?.instance?.is_setup_done === false) return <InstanceNotReady />;

View File

@ -122,7 +122,7 @@ const ForgotPasswordPage: NextPageWithLayout = () => {
</Link> </Link>
</div> </div>
</div> </div>
<div className="flex-grow container mx-auto max-w-lg px-10 lg:max-w-md lg:px-5 py-10"> <div className="flex-grow container mx-auto max-w-lg px-10 lg:max-w-md lg:px-5 py-10 lg:pt-28 transition-all">
<div className="relative flex flex-col space-y-6"> <div className="relative flex flex-col space-y-6">
<div className="text-center space-y-1 py-4"> <div className="text-center space-y-1 py-4">
<h3 className="flex gap-4 justify-center text-3xl font-bold text-onboarding-text-100"> <h3 className="flex gap-4 justify-center text-3xl font-bold text-onboarding-text-100">
@ -176,7 +176,7 @@ const ForgotPasswordPage: NextPageWithLayout = () => {
> >
{resendTimerCode > 0 ? `Resend in ${resendTimerCode} seconds` : "Send reset link"} {resendTimerCode > 0 ? `Resend in ${resendTimerCode} seconds` : "Send reset link"}
</Button> </Button>
<Link href="/" className={cn("w-full", getButtonStyling("link-neutral", "lg"))}> <Link href="/accounts/sign-in" className={cn("w-full", getButtonStyling("link-neutral", "lg"))}>
Back to sign in Back to sign in
</Link> </Link>
</form> </form>

View File

@ -90,7 +90,7 @@ const ResetPasswordPage: NextPageWithLayout = () => {
<span className="text-2xl font-semibold sm:text-3xl">Plane</span> <span className="text-2xl font-semibold sm:text-3xl">Plane</span>
</div> </div>
</div> </div>
<div className="flex-grow container mx-auto max-w-lg px-10 lg:max-w-md lg:px-5 py-10"> <div className="flex-grow container mx-auto max-w-lg px-10 lg:max-w-md lg:px-5 py-10 lg:pt-28 transition-all">
<div className="relative flex flex-col space-y-6"> <div className="relative flex flex-col space-y-6">
<div className="text-center space-y-1 py-4"> <div className="text-center space-y-1 py-4">
<h3 className="flex gap-4 justify-center text-3xl font-bold text-onboarding-text-100"> <h3 className="flex gap-4 justify-center text-3xl font-bold text-onboarding-text-100">

View File

@ -111,7 +111,7 @@ const SetPasswordPage: NextPageWithLayout = observer(() => {
<span className="text-2xl font-semibold sm:text-3xl">Plane</span> <span className="text-2xl font-semibold sm:text-3xl">Plane</span>
</div> </div>
</div> </div>
<div className="flex-grow container mx-auto max-w-lg px-10 lg:max-w-md lg:px-5 py-10"> <div className="flex-grow container mx-auto max-w-lg px-10 lg:max-w-md lg:px-5 py-10 lg:pt-28 transition-all">
<div className="relative flex flex-col space-y-6"> <div className="relative flex flex-col space-y-6">
<div className="text-center space-y-1 py-4"> <div className="text-center space-y-1 py-4">
<h3 className="flex gap-4 justify-center text-3xl font-bold text-onboarding-text-100"> <h3 className="flex gap-4 justify-center text-3xl font-bold text-onboarding-text-100">

View File

@ -4,12 +4,12 @@ import Link from "next/link";
// ui // ui
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
// components // components
import { SignInAuthRoot } from "@/components/account"; import { AuthRoot } from "@/components/account";
import { PageHead } from "@/components/core"; import { PageHead } from "@/components/core";
// constants // constants
import { NAVIGATE_TO_SIGNUP } from "@/constants/event-tracker"; import { NAVIGATE_TO_SIGNUP } from "@/constants/event-tracker";
// helpers // helpers
import { EPageTypes } from "@/helpers/authentication.helper"; import { EAuthModes, EPageTypes } from "@/helpers/authentication.helper";
// hooks // hooks
import { useEventTracker } from "@/hooks/store"; import { useEventTracker } from "@/hooks/store";
// layouts // layouts
@ -58,8 +58,8 @@ const SignInPage: NextPageWithLayout = observer(() => {
</Link> </Link>
</div> </div>
</div> </div>
<div className="flex-grow container mx-auto max-w-lg px-10 lg:max-w-md lg:px-5 py-10"> <div className="flex-grow container mx-auto max-w-lg px-10 lg:max-w-md lg:px-5 py-10 lg:pt-28 transition-all">
<SignInAuthRoot /> <AuthRoot authMode={EAuthModes.SIGN_IN} />
</div> </div>
</div> </div>
</div> </div>

View File

@ -5,12 +5,12 @@ import Link from "next/link";
// ui // ui
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
// components // components
import { SignUpAuthRoot } from "@/components/account"; import { AuthRoot } from "@/components/account";
import { PageHead } from "@/components/core"; import { PageHead } from "@/components/core";
// constants // constants
import { NAVIGATE_TO_SIGNIN } from "@/constants/event-tracker"; import { NAVIGATE_TO_SIGNIN } from "@/constants/event-tracker";
// helpers // helpers
import { EPageTypes } from "@/helpers/authentication.helper"; import { EAuthModes, EPageTypes } from "@/helpers/authentication.helper";
// hooks // hooks
import { useEventTracker } from "@/hooks/store"; import { useEventTracker } from "@/hooks/store";
// layouts // layouts
@ -56,8 +56,8 @@ const HomePage: NextPageWithLayout = observer(() => {
</Link> </Link>
</div> </div>
</div> </div>
<div className="flex-grow container mx-auto max-w-lg px-10 lg:max-w-md lg:px-5 py-10"> <div className="flex-grow container mx-auto max-w-lg px-10 lg:max-w-md lg:px-5 py-10 lg:pt-28 transition-all">
<SignUpAuthRoot /> <AuthRoot authMode={EAuthModes.SIGN_UP} />
</div> </div>
</div> </div>
</div> </div>