diff --git a/packages/constants/src/auth.ts b/packages/constants/src/auth.ts index bcdda31b4d..1d704c4ccf 100644 --- a/packages/constants/src/auth.ts +++ b/packages/constants/src/auth.ts @@ -11,8 +11,7 @@ export const SPACE_PASSWORD_CRITERIA = [ { key: "min_8_char", label: "Min 8 characters", - isCriteriaValid: (password: string) => - password.length >= PASSWORD_MIN_LENGTH, + isCriteriaValid: (password: string) => password.length >= PASSWORD_MIN_LENGTH, }, // { // key: "min_1_upper_case", diff --git a/packages/constants/src/endpoints.ts b/packages/constants/src/endpoints.ts index 0822e06cbe..d43a661c36 100644 --- a/packages/constants/src/endpoints.ts +++ b/packages/constants/src/endpoints.ts @@ -22,11 +22,9 @@ export const SILO_BASE_URL = process.env.NEXT_PUBLIC_SILO_BASE_URL || ""; export const SILO_BASE_PATH = process.env.NEXT_PUBLIC_SILO_BASE_PATH || ""; export const SILO_URL = encodeURI(`${SILO_BASE_URL}${SILO_BASE_PATH}/`); // plane website url -export const WEBSITE_URL = - process.env.NEXT_PUBLIC_WEBSITE_URL || "https://plane.so"; +export const WEBSITE_URL = process.env.NEXT_PUBLIC_WEBSITE_URL || "https://plane.so"; // support email -export const SUPPORT_EMAIL = - process.env.NEXT_PUBLIC_SUPPORT_EMAIL || "support@plane.so"; +export const SUPPORT_EMAIL = process.env.NEXT_PUBLIC_SUPPORT_EMAIL || "support@plane.so"; // marketing links export const MARKETING_PRICING_PAGE_LINK = "https://plane.so/pricing"; export const MARKETING_CONTACT_US_PAGE_LINK = "https://plane.so/contact"; diff --git a/packages/constants/src/index.ts b/packages/constants/src/index.ts index 7ca2abf78d..15e4772ce1 100644 --- a/packages/constants/src/index.ts +++ b/packages/constants/src/index.ts @@ -49,3 +49,4 @@ export * from "./customers"; export * from "./projects-extended"; export * from "./state-extended"; export * from "./analytics"; +export * from "./mobile"; diff --git a/packages/constants/src/mobile/auth.ts b/packages/constants/src/mobile/auth.ts new file mode 100644 index 0000000000..75a86014e1 --- /dev/null +++ b/packages/constants/src/mobile/auth.ts @@ -0,0 +1,100 @@ +// error codes for mobile auth +export const EMobileAuthErrorCodes = { + // Global + INSTANCE_NOT_CONFIGURED: "5000", + INVALID_EMAIL: "5005", + EMAIL_REQUIRED: "5010", + SIGNUP_DISABLED: "5015", + MAGIC_LINK_LOGIN_DISABLED: "5016", + PASSWORD_LOGIN_DISABLED: "5018", + USER_ACCOUNT_DEACTIVATED: "5019", + // Password strength + INVALID_PASSWORD: "5020", + SMTP_NOT_CONFIGURED: "5025", + // Sign Up + USER_ALREADY_EXIST: "5030", + AUTHENTICATION_FAILED_SIGN_UP: "5035", + REQUIRED_EMAIL_PASSWORD_SIGN_UP: "5040", + INVALID_EMAIL_SIGN_UP: "5045", + INVALID_EMAIL_MAGIC_SIGN_UP: "5050", + MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED: "5055", + // Sign In + USER_DOES_NOT_EXIST: "5060", + AUTHENTICATION_FAILED_SIGN_IN: "5065", + REQUIRED_EMAIL_PASSWORD_SIGN_IN: "5070", + INVALID_EMAIL_SIGN_IN: "5075", + INVALID_EMAIL_MAGIC_SIGN_IN: "5080", + MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED: "5085", + // Both Sign in and Sign up for magic + INVALID_MAGIC_CODE_SIGN_IN: "5090", + INVALID_MAGIC_CODE_SIGN_UP: "5092", + EXPIRED_MAGIC_CODE_SIGN_IN: "5095", + EXPIRED_MAGIC_CODE_SIGN_UP: "5097", + EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_IN: "5100", + EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_UP: "5102", + // Oauth + OAUTH_NOT_CONFIGURED: "5104", + GOOGLE_NOT_CONFIGURED: "5105", + GITHUB_NOT_CONFIGURED: "5110", + GITLAB_NOT_CONFIGURED: "5111", + GOOGLE_OAUTH_PROVIDER_ERROR: "5115", + GITHUB_OAUTH_PROVIDER_ERROR: "5120", + GITLAB_OAUTH_PROVIDER_ERROR: "5121", + // Reset Password + INVALID_PASSWORD_TOKEN: "5125", + EXPIRED_PASSWORD_TOKEN: "5130", + // Change password + INCORRECT_OLD_PASSWORD: "5135", + MISSING_PASSWORD: "5138", + INVALID_NEW_PASSWORD: "5140", + // set passowrd + PASSWORD_ALREADY_SET: "5145", + // Admin + ADMIN_ALREADY_EXIST: "5150", + REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME: "5155", + INVALID_ADMIN_EMAIL: "5160", + INVALID_ADMIN_PASSWORD: "5165", + REQUIRED_ADMIN_EMAIL_PASSWORD: "5170", + ADMIN_AUTHENTICATION_FAILED: "5175", + ADMIN_USER_ALREADY_EXIST: "5180", + ADMIN_USER_DOES_NOT_EXIST: "5185", + ADMIN_USER_DEACTIVATED: "5190", + // Rate limit + RATE_LIMIT_EXCEEDED: "5900", + // mobile specific codes + USER_NOT_ONBOARDED: "6000", + TOKEN_NOT_SET: "6005", + MOBILE_SIGNUP_DISABLED: "6010", +}; +export type TMobileAuthErrorCodes = (typeof EMobileAuthErrorCodes)[keyof typeof EMobileAuthErrorCodes]; + +export const EMobileErrorAlertType = { + 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", +} as const; +export type TMobileErrorAlertType = (typeof EMobileErrorAlertType)[keyof typeof EMobileErrorAlertType]; + +export type TMobileAuthErrorInfo = { + type: TMobileErrorAlertType; + code: TMobileAuthErrorCodes; + title: string; + message: any; +}; + +// mobile auth modes and steps +export const EMobileAuthModes = { + SIGN_IN: "SIGN_IN", + SIGN_UP: "SIGN_UP", +} as const; +export type TMobileAuthModes = (typeof EMobileAuthModes)[keyof typeof EMobileAuthModes]; + +export const EMobileAuthSteps = { + EMAIL: "EMAIL", + PASSWORD: "PASSWORD", + UNIQUE_CODE: "UNIQUE_CODE", +} as const; +export type TMobileAuthSteps = (typeof EMobileAuthSteps)[keyof typeof EMobileAuthSteps]; diff --git a/packages/constants/src/mobile/index.ts b/packages/constants/src/mobile/index.ts new file mode 100644 index 0000000000..97ccf76494 --- /dev/null +++ b/packages/constants/src/mobile/index.ts @@ -0,0 +1 @@ +export * from "./auth"; diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 78f18ef561..63d1be630c 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -21,3 +21,4 @@ export * from "./work-item-properties"; export * from "./work-item-types"; export * from "./workflow"; export * from "./workspace"; +export * from "./mobile"; diff --git a/packages/utils/src/mobile/auth.tsx b/packages/utils/src/mobile/auth.tsx new file mode 100644 index 0000000000..ffda3559d0 --- /dev/null +++ b/packages/utils/src/mobile/auth.tsx @@ -0,0 +1,314 @@ +import { ReactNode } from "react"; +import { + TMobileAuthErrorCodes, + TMobileAuthErrorInfo, + EMobileAuthErrorCodes, + EMobileErrorAlertType, + SUPPORT_EMAIL, +} from "@plane/constants"; + +const mobileAuthErrorCodeMessages: { + [key in TMobileAuthErrorCodes]: { title: string; message: (email?: string | undefined) => ReactNode }; +} = { + // global + [EMobileAuthErrorCodes.INSTANCE_NOT_CONFIGURED]: { + title: `Instance not configured`, + message: () => `Instance not configured. Please contact your administrator.`, + }, + [EMobileAuthErrorCodes.INVALID_EMAIL]: { + title: `Invalid email`, + message: () => `Invalid email. Please try again.`, + }, + [EMobileAuthErrorCodes.EMAIL_REQUIRED]: { + title: `Email required`, + message: () => `Email required. Please try again.`, + }, + [EMobileAuthErrorCodes.SIGNUP_DISABLED]: { + title: `Sign up disabled`, + message: () => `Sign up disabled. Please contact your administrator.`, + }, + [EMobileAuthErrorCodes.MAGIC_LINK_LOGIN_DISABLED]: { + title: `Magic link login disabled`, + message: () => `Magic link login disabled. Please contact your administrator.`, + }, + [EMobileAuthErrorCodes.PASSWORD_LOGIN_DISABLED]: { + title: `Password login disabled`, + message: () => `Password login disabled. Please contact your administrator.`, + }, + [EMobileAuthErrorCodes.USER_ACCOUNT_DEACTIVATED]: { + title: `User account deactivated`, + message: () => `User account deactivated. Please contact ${!!SUPPORT_EMAIL ? SUPPORT_EMAIL : "administrator"}.`, + }, + [EMobileAuthErrorCodes.INVALID_PASSWORD]: { + title: `Invalid password`, + message: () => `Invalid password. Please try again.`, + }, + [EMobileAuthErrorCodes.SMTP_NOT_CONFIGURED]: { + title: `SMTP not configured`, + message: () => `SMTP not configured. Please contact your administrator.`, + }, + + // sign up + [EMobileAuthErrorCodes.USER_ALREADY_EXIST]: { + title: `User already exists`, + message: () => `Your account is already registered. Please try again.`, + }, + [EMobileAuthErrorCodes.REQUIRED_EMAIL_PASSWORD_SIGN_UP]: { + title: `Email and password required`, + message: () => `Email and password required. Please try again.`, + }, + [EMobileAuthErrorCodes.AUTHENTICATION_FAILED_SIGN_UP]: { + title: `Authentication failed`, + message: () => `Authentication failed. Please try again.`, + }, + [EMobileAuthErrorCodes.INVALID_EMAIL_SIGN_UP]: { + title: `Invalid email`, + message: () => `Invalid email. Please try again.`, + }, + [EMobileAuthErrorCodes.MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED]: { + title: `Email and code required`, + message: () => `Email and code required. Please try again.`, + }, + [EMobileAuthErrorCodes.INVALID_EMAIL_MAGIC_SIGN_UP]: { + title: `Invalid email`, + message: () => `Invalid email. Please try again.`, + }, + + [EMobileAuthErrorCodes.USER_DOES_NOT_EXIST]: { + title: `User does not exist`, + message: () => `No account found. Please try again.`, + }, + [EMobileAuthErrorCodes.REQUIRED_EMAIL_PASSWORD_SIGN_IN]: { + title: `Email and password required`, + message: () => `Email and password required. Please try again.`, + }, + [EMobileAuthErrorCodes.AUTHENTICATION_FAILED_SIGN_IN]: { + title: `Authentication failed`, + message: () => `Authentication failed. Please try again.`, + }, + [EMobileAuthErrorCodes.INVALID_EMAIL_SIGN_IN]: { + title: `Invalid email`, + message: () => `Invalid email. Please try again.`, + }, + [EMobileAuthErrorCodes.MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED]: { + title: `Email and code required`, + message: () => `Email and code required. Please try again.`, + }, + [EMobileAuthErrorCodes.INVALID_EMAIL_MAGIC_SIGN_IN]: { + title: `Invalid email`, + message: () => `Invalid email. Please try again.`, + }, + + // Both Sign in and Sign up + [EMobileAuthErrorCodes.INVALID_MAGIC_CODE_SIGN_IN]: { + title: `Authentication failed`, + message: () => `Invalid magic code. Please try again.`, + }, + [EMobileAuthErrorCodes.INVALID_MAGIC_CODE_SIGN_UP]: { + title: `Authentication failed`, + message: () => `Invalid magic code. Please try again.`, + }, + [EMobileAuthErrorCodes.EXPIRED_MAGIC_CODE_SIGN_IN]: { + title: `Expired magic code`, + message: () => `Expired magic code. Please try again.`, + }, + [EMobileAuthErrorCodes.EXPIRED_MAGIC_CODE_SIGN_UP]: { + title: `Expired magic code`, + message: () => `Expired magic code. Please try again.`, + }, + [EMobileAuthErrorCodes.EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_IN]: { + title: `Expired magic code`, + message: () => `Expired magic code. Please try again.`, + }, + [EMobileAuthErrorCodes.EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_UP]: { + title: `Expired magic code`, + message: () => `Expired magic code. Please try again.`, + }, + + // Oauth + [EMobileAuthErrorCodes.OAUTH_NOT_CONFIGURED]: { + title: `OAuth not configured`, + message: () => `OAuth not configured. Please contact your administrator.`, + }, + [EMobileAuthErrorCodes.GOOGLE_NOT_CONFIGURED]: { + title: `Google not configured`, + message: () => `Google not configured. Please contact your administrator.`, + }, + [EMobileAuthErrorCodes.GITHUB_NOT_CONFIGURED]: { + title: `GitHub not configured`, + message: () => `GitHub not configured. Please contact your administrator.`, + }, + [EMobileAuthErrorCodes.GITLAB_NOT_CONFIGURED]: { + title: `GitLab not configured`, + message: () => `GitLab not configured. Please contact your administrator.`, + }, + [EMobileAuthErrorCodes.GOOGLE_OAUTH_PROVIDER_ERROR]: { + title: `Google OAuth provider error`, + message: () => `Google OAuth provider error. Please try again.`, + }, + [EMobileAuthErrorCodes.GITHUB_OAUTH_PROVIDER_ERROR]: { + title: `GitHub OAuth provider error`, + message: () => `GitHub OAuth provider error. Please try again.`, + }, + [EMobileAuthErrorCodes.GITLAB_OAUTH_PROVIDER_ERROR]: { + title: `GitLab OAuth provider error`, + message: () => `GitLab OAuth provider error. Please try again.`, + }, + + // Reset Password + [EMobileAuthErrorCodes.INVALID_PASSWORD_TOKEN]: { + title: `Invalid password token`, + message: () => `Invalid password token.`, + }, + [EMobileAuthErrorCodes.EXPIRED_PASSWORD_TOKEN]: { + title: `Expired password token`, + message: () => `Expired password token. Please try again.`, + }, + + // Change password + [EMobileAuthErrorCodes.MISSING_PASSWORD]: { + title: `Password required`, + message: () => `Password required. Please try again.`, + }, + [EMobileAuthErrorCodes.INCORRECT_OLD_PASSWORD]: { + title: `Incorrect old password`, + message: () => `Incorrect old password. Please try again.`, + }, + [EMobileAuthErrorCodes.INVALID_NEW_PASSWORD]: { + title: `Invalid new password`, + message: () => `Invalid new password. Please try again.`, + }, + + // set password + [EMobileAuthErrorCodes.PASSWORD_ALREADY_SET]: { + title: `Password already set`, + message: () => `Password already set. Please try again.`, + }, + + // admin + [EMobileAuthErrorCodes.ADMIN_ALREADY_EXIST]: { + title: `Admin already exists`, + message: () => `Admin already exists. Please try again.`, + }, + [EMobileAuthErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME]: { + title: `Email, password and first name required`, + message: () => `Email, password and first name required. Please try again.`, + }, + [EMobileAuthErrorCodes.INVALID_ADMIN_EMAIL]: { + title: `Invalid admin email`, + message: () => `Invalid admin email. Please try again.`, + }, + [EMobileAuthErrorCodes.INVALID_ADMIN_PASSWORD]: { + title: `Invalid admin password`, + message: () => `Invalid admin password. Please try again.`, + }, + [EMobileAuthErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD]: { + title: `Email and password required`, + message: () => `Email and password required. Please try again.`, + }, + [EMobileAuthErrorCodes.ADMIN_AUTHENTICATION_FAILED]: { + title: `Authentication failed`, + message: () => `Authentication failed. Please try again.`, + }, + [EMobileAuthErrorCodes.ADMIN_USER_ALREADY_EXIST]: { + title: `Admin user already exists`, + message: () => `Admin user already exists. Please try again.`, + }, + [EMobileAuthErrorCodes.ADMIN_USER_DOES_NOT_EXIST]: { + title: `Admin user does not exist`, + message: () => `Admin user does not exist. Please try again.`, + }, + [EMobileAuthErrorCodes.ADMIN_USER_DEACTIVATED]: { + title: `Admin user deactivated`, + message: () => `Your account is deactivated`, + }, + [EMobileAuthErrorCodes.RATE_LIMIT_EXCEEDED]: { + title: "", + message: () => `Rate limit exceeded. Please try again later.`, + }, + + // mobile specific codes + [EMobileAuthErrorCodes.USER_NOT_ONBOARDED]: { + title: `User not onboarded`, + message: () => `User not onboarded. Please try again.`, + }, + [EMobileAuthErrorCodes.TOKEN_NOT_SET]: { + title: `Token not set`, + message: () => `Token not set. Please try again.`, + }, + [EMobileAuthErrorCodes.MOBILE_SIGNUP_DISABLED]: { + title: `Mobile sign up disabled`, + message: () => `Mobile sign up disabled. Please contact your administrator.`, + }, +}; + +export const mobileAuthErrorHandler = ( + errorCode: TMobileAuthErrorCodes, + email?: string | undefined +): TMobileAuthErrorInfo | undefined => { + const bannerAlertErrorCodes = [ + EMobileAuthErrorCodes.INSTANCE_NOT_CONFIGURED, + EMobileAuthErrorCodes.INVALID_EMAIL, + EMobileAuthErrorCodes.EMAIL_REQUIRED, + EMobileAuthErrorCodes.SIGNUP_DISABLED, + EMobileAuthErrorCodes.MAGIC_LINK_LOGIN_DISABLED, + EMobileAuthErrorCodes.PASSWORD_LOGIN_DISABLED, + EMobileAuthErrorCodes.USER_ACCOUNT_DEACTIVATED, + EMobileAuthErrorCodes.INVALID_PASSWORD, + EMobileAuthErrorCodes.SMTP_NOT_CONFIGURED, + EMobileAuthErrorCodes.USER_ALREADY_EXIST, + EMobileAuthErrorCodes.AUTHENTICATION_FAILED_SIGN_UP, + EMobileAuthErrorCodes.REQUIRED_EMAIL_PASSWORD_SIGN_UP, + EMobileAuthErrorCodes.INVALID_EMAIL_SIGN_UP, + EMobileAuthErrorCodes.INVALID_EMAIL_MAGIC_SIGN_UP, + EMobileAuthErrorCodes.MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED, + EMobileAuthErrorCodes.USER_DOES_NOT_EXIST, + EMobileAuthErrorCodes.AUTHENTICATION_FAILED_SIGN_IN, + EMobileAuthErrorCodes.REQUIRED_EMAIL_PASSWORD_SIGN_IN, + EMobileAuthErrorCodes.INVALID_EMAIL_SIGN_IN, + EMobileAuthErrorCodes.INVALID_EMAIL_MAGIC_SIGN_IN, + EMobileAuthErrorCodes.MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED, + EMobileAuthErrorCodes.INVALID_MAGIC_CODE_SIGN_IN, + EMobileAuthErrorCodes.INVALID_MAGIC_CODE_SIGN_UP, + EMobileAuthErrorCodes.EXPIRED_MAGIC_CODE_SIGN_IN, + EMobileAuthErrorCodes.EXPIRED_MAGIC_CODE_SIGN_UP, + EMobileAuthErrorCodes.EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_IN, + EMobileAuthErrorCodes.EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_UP, + EMobileAuthErrorCodes.OAUTH_NOT_CONFIGURED, + EMobileAuthErrorCodes.GOOGLE_NOT_CONFIGURED, + EMobileAuthErrorCodes.GITHUB_NOT_CONFIGURED, + EMobileAuthErrorCodes.GITLAB_NOT_CONFIGURED, + EMobileAuthErrorCodes.GOOGLE_OAUTH_PROVIDER_ERROR, + EMobileAuthErrorCodes.GITHUB_OAUTH_PROVIDER_ERROR, + EMobileAuthErrorCodes.GITLAB_OAUTH_PROVIDER_ERROR, + EMobileAuthErrorCodes.INVALID_PASSWORD_TOKEN, + EMobileAuthErrorCodes.EXPIRED_PASSWORD_TOKEN, + EMobileAuthErrorCodes.INCORRECT_OLD_PASSWORD, + EMobileAuthErrorCodes.MISSING_PASSWORD, + EMobileAuthErrorCodes.INVALID_NEW_PASSWORD, + EMobileAuthErrorCodes.PASSWORD_ALREADY_SET, + EMobileAuthErrorCodes.ADMIN_ALREADY_EXIST, + EMobileAuthErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME, + EMobileAuthErrorCodes.INVALID_ADMIN_EMAIL, + EMobileAuthErrorCodes.INVALID_ADMIN_PASSWORD, + EMobileAuthErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD, + EMobileAuthErrorCodes.ADMIN_AUTHENTICATION_FAILED, + EMobileAuthErrorCodes.ADMIN_USER_ALREADY_EXIST, + EMobileAuthErrorCodes.ADMIN_USER_DOES_NOT_EXIST, + EMobileAuthErrorCodes.ADMIN_USER_DEACTIVATED, + EMobileAuthErrorCodes.RATE_LIMIT_EXCEEDED, + EMobileAuthErrorCodes.USER_NOT_ONBOARDED, + EMobileAuthErrorCodes.TOKEN_NOT_SET, + EMobileAuthErrorCodes.MOBILE_SIGNUP_DISABLED, + ]; + + if (bannerAlertErrorCodes.includes(errorCode)) + return { + type: EMobileErrorAlertType.BANNER_ALERT, + code: errorCode, + title: mobileAuthErrorCodeMessages[errorCode]?.title || "Error", + message: mobileAuthErrorCodeMessages[errorCode]?.message(email) || "Something went wrong. Please try again.", + }; + + return undefined; +}; diff --git a/packages/utils/src/mobile/index.ts b/packages/utils/src/mobile/index.ts new file mode 100644 index 0000000000..97ccf76494 --- /dev/null +++ b/packages/utils/src/mobile/index.ts @@ -0,0 +1 @@ +export * from "./auth"; diff --git a/web/ee/components/mobile/authentication/banner.tsx b/web/ee/components/mobile/authentication/banner.tsx new file mode 100644 index 0000000000..ade66f6db2 --- /dev/null +++ b/web/ee/components/mobile/authentication/banner.tsx @@ -0,0 +1,38 @@ +import { FC } from "react"; +import { Info, X } from "lucide-react"; +import { TMobileAuthErrorInfo } from "@plane/constants"; +// plane imports +import { useTranslation } from "@plane/i18n"; + +type TMobileAuthBanner = { + bannerData: TMobileAuthErrorInfo | undefined; + handleBannerData?: (bannerData: TMobileAuthErrorInfo | undefined) => void; +}; + +export const MobileAuthBanner: FC = (props) => { + const { bannerData, handleBannerData } = props; + // translation + const { t } = useTranslation(); + + if (!bannerData) return <>; + + return ( +
+
+ +
+

{bannerData?.message}

+ +
+ ); +}; diff --git a/web/ee/components/mobile/authentication/forms/email-confirmation.tsx b/web/ee/components/mobile/authentication/forms/email-confirmation.tsx index 59103a3e93..d81b9327e3 100644 --- a/web/ee/components/mobile/authentication/forms/email-confirmation.tsx +++ b/web/ee/components/mobile/authentication/forms/email-confirmation.tsx @@ -2,10 +2,17 @@ import { FC, FormEvent, useMemo, useRef, useState } from "react"; import { CircleAlert, XCircle } from "lucide-react"; +import { + EMobileAuthSteps, + EMobileAuthModes, + TMobileAuthSteps, + TMobileAuthModes, + TMobileAuthErrorInfo, +} from "@plane/constants"; import { IEmailCheckData } from "@plane/types"; import { Button, Input, Spinner } from "@plane/ui"; // helpers -import { authErrorHandler, EAuthModes, EAuthSteps, TAuthErrorInfo } from "@/helpers/authentication.helper"; +import { authErrorHandler } from "@/helpers/authentication.helper"; import { cn } from "@/helpers/common.helper"; import { checkEmailValidity } from "@/helpers/string.helper"; // plane web services @@ -14,9 +21,9 @@ import mobileAuthService from "@/plane-web/services/mobile.service"; type TMobileAuthEmailValidationForm = { email: string; handleEmail: (value: string) => void; - handleAuthStep: (value: EAuthSteps) => void; - handleAuthMode: (value: EAuthModes) => void; - handleErrorInfo: (value: TAuthErrorInfo | undefined) => void; + handleAuthStep: (value: TMobileAuthSteps) => void; + handleAuthMode: (value: TMobileAuthModes) => void; + handleErrorInfo: (value: TMobileAuthErrorInfo | undefined) => void; generateEmailUniqueCode: (email: string) => Promise<{ code: string } | undefined>; }; @@ -83,18 +90,18 @@ export const MobileAuthEmailValidationForm: FC = .then(async (response) => { // setting auth mode if (response.existing) { - handleAuthMode(EAuthModes.SIGN_IN); + handleAuthMode(EMobileAuthModes.SIGN_IN); } else { - handleAuthMode(EAuthModes.SIGN_UP); + handleAuthMode(EMobileAuthModes.SIGN_UP); } // setting auth step if (response.status === "MAGIC_CODE") { - handleAuthStep(EAuthSteps.UNIQUE_CODE); + handleAuthStep(EMobileAuthSteps.UNIQUE_CODE); // generating unique code generateEmailUniqueCode(email); } else if (response.status === "CREDENTIAL") { - handleAuthStep(EAuthSteps.PASSWORD); + handleAuthStep(EMobileAuthSteps.PASSWORD); } }) .catch((error) => { diff --git a/web/ee/components/mobile/authentication/forms/password.tsx b/web/ee/components/mobile/authentication/forms/password.tsx index cbcd71f204..fef231e8a6 100644 --- a/web/ee/components/mobile/authentication/forms/password.tsx +++ b/web/ee/components/mobile/authentication/forms/password.tsx @@ -2,20 +2,20 @@ import { FC, useEffect, useRef, useState } from "react"; import { Eye, EyeOff, XCircle } from "lucide-react"; +import { EMobileAuthSteps, EMobileAuthModes, TMobileAuthSteps, TMobileAuthModes } from "@plane/constants"; import { TMobileCSRFToken } from "@plane/types"; import { Button, Input, Spinner } from "@plane/ui"; // helpers -import { EAuthSteps, EAuthModes } from "@/helpers/authentication.helper"; import { API_BASE_URL } from "@/helpers/common.helper"; // services import mobileAuthService from "@/plane-web/services/mobile.service"; type TMobileAuthPasswordForm = { - authMode: EAuthModes; + authMode: TMobileAuthModes; invitationId: string | undefined; email: string; handleEmail: (value: string) => void; - handleAuthStep: (value: EAuthSteps) => void; + handleAuthStep: (value: TMobileAuthSteps) => void; generateEmailUniqueCode: (email: string) => Promise<{ code: string } | undefined>; isSMTPConfigured: boolean; }; @@ -65,11 +65,11 @@ export const MobileAuthPasswordForm: FC = (props) => { const handleEmailClear = () => { handleEmail(""); - handleAuthStep(EAuthSteps.EMAIL); + handleAuthStep(EMobileAuthSteps.EMAIL); }; const redirectToUniqueCodeSignIn = () => { - handleAuthStep(EAuthSteps.UNIQUE_CODE); + handleAuthStep(EMobileAuthSteps.UNIQUE_CODE); // generate unique code generateEmailUniqueCode(email); }; @@ -79,7 +79,7 @@ export const MobileAuthPasswordForm: FC = (props) => { ref={authFormRef} className="mt-5 space-y-4" method="POST" - action={`${API_BASE_URL}/auth/mobile/${authMode === EAuthModes.SIGN_UP ? "sign-up" : "sign-in"}/`} + action={`${API_BASE_URL}/auth/mobile/${authMode === EMobileAuthModes.SIGN_UP ? "sign-up" : "sign-in"}/`} onSubmit={async (event) => { event.preventDefault(); // Prevent form from submitting by default setIsSubmitting(true); diff --git a/web/ee/components/mobile/authentication/forms/unique-code.tsx b/web/ee/components/mobile/authentication/forms/unique-code.tsx index d4797c6964..76bd8f7df5 100644 --- a/web/ee/components/mobile/authentication/forms/unique-code.tsx +++ b/web/ee/components/mobile/authentication/forms/unique-code.tsx @@ -2,9 +2,9 @@ import { FC, useEffect, useRef, useState } from "react"; import { CircleCheck, XCircle } from "lucide-react"; +import { EMobileAuthSteps, EMobileAuthModes, TMobileAuthSteps, TMobileAuthModes } from "@plane/constants"; import { Button, Input, Spinner } from "@plane/ui"; // helpers -import { EAuthSteps, EAuthModes } from "@/helpers/authentication.helper"; import { API_BASE_URL } from "@/helpers/common.helper"; // hooks import useTimer from "@/hooks/use-timer"; @@ -12,11 +12,11 @@ import useTimer from "@/hooks/use-timer"; import mobileAuthService from "@/plane-web/services/mobile.service"; type TMobileAuthUniqueCodeForm = { - authMode: EAuthModes; + authMode: TMobileAuthModes; invitationId: string | undefined; email: string; handleEmail: (value: string) => void; - handleAuthStep: (value: EAuthSteps) => void; + handleAuthStep: (value: TMobileAuthSteps) => void; generateEmailUniqueCode: (email: string) => Promise<{ code: string } | undefined>; }; @@ -82,7 +82,7 @@ export const MobileAuthUniqueCodeForm: FC = (props) = const handleEmailClear = () => { handleEmail(""); - handleAuthStep(EAuthSteps.EMAIL); + handleAuthStep(EMobileAuthSteps.EMAIL); }; return ( @@ -90,7 +90,7 @@ export const MobileAuthUniqueCodeForm: FC = (props) = ref={authFormRef} className="mt-5 space-y-4" method="POST" - action={`${API_BASE_URL}/auth/mobile/${authMode === EAuthModes.SIGN_UP ? "magic-sign-up" : "magic-sign-in"}/`} + action={`${API_BASE_URL}/auth/mobile/${authMode === EMobileAuthModes.SIGN_UP ? "magic-sign-up" : "magic-sign-in"}/`} onSubmit={async (event) => { event.preventDefault(); // Prevent form from submitting by default setIsSubmitting(true); diff --git a/web/ee/components/mobile/authentication/header.tsx b/web/ee/components/mobile/authentication/header.tsx index 4a398b5e12..582e28e2fe 100644 --- a/web/ee/components/mobile/authentication/header.tsx +++ b/web/ee/components/mobile/authentication/header.tsx @@ -2,45 +2,44 @@ import { FC } from "react"; import Image from "next/image"; +import { EMobileAuthModes, EMobileAuthSteps, TMobileAuthModes, TMobileAuthSteps } from "@plane/constants"; import { TMobileWorkspaceInvitation } from "@plane/types"; -// helpers -import { EAuthModes, EAuthSteps } from "@/helpers/authentication.helper"; // assets import planeLogo from "@/public/plane-logos/blue-without-text.png"; const AUTH_SIGNUP_HEADER_CONTENT_OPTIONS = { - [EAuthSteps.EMAIL]: { + [EMobileAuthSteps.EMAIL]: { title: "sign up", description: "", }, - [EAuthSteps.UNIQUE_CODE]: { + [EMobileAuthSteps.UNIQUE_CODE]: { title: "Sign up", description: "Sign up using a unique code sent to the email address above.", }, - [EAuthSteps.PASSWORD]: { + [EMobileAuthSteps.PASSWORD]: { title: "Sign up", description: "Sign up using an email-password combination.", }, }; const AUTH_LOGIN_HEADER_CONTENT_OPTIONS = { - [EAuthSteps.EMAIL]: { + [EMobileAuthSteps.EMAIL]: { title: "Log in or sign up", description: "", }, - [EAuthSteps.UNIQUE_CODE]: { + [EMobileAuthSteps.UNIQUE_CODE]: { title: "Log in or sign up", description: "Log in using a unique code sent to the email address above.", }, - [EAuthSteps.PASSWORD]: { + [EMobileAuthSteps.PASSWORD]: { title: "Log in or sign up", description: "Use your email-password combination to log in.", }, }; type TMobileAuthHeader = { - authMode: EAuthModes; - authStep: EAuthSteps; + authMode: TMobileAuthModes; + authStep: TMobileAuthSteps; invitationDetails: TMobileWorkspaceInvitation | undefined; }; @@ -50,7 +49,7 @@ export const MobileAuthHeader: FC = (props) => { // derived values const content = - authMode === EAuthModes.SIGN_UP + authMode === EMobileAuthModes.SIGN_UP ? AUTH_SIGNUP_HEADER_CONTENT_OPTIONS[authStep] : AUTH_LOGIN_HEADER_CONTENT_OPTIONS[authStep]; const isInvitation = invitationDetails?.id || undefined; diff --git a/web/ee/components/mobile/authentication/index.ts b/web/ee/components/mobile/authentication/index.ts index 07df351bdd..afa52d36ba 100644 --- a/web/ee/components/mobile/authentication/index.ts +++ b/web/ee/components/mobile/authentication/index.ts @@ -8,4 +8,6 @@ export * from "./terms-and-conditions"; export * from "./header"; +export * from "./banner"; + export * from "./wrappers"; diff --git a/web/ee/components/mobile/authentication/root.tsx b/web/ee/components/mobile/authentication/root.tsx index d1d205ac67..6313ea4e39 100644 --- a/web/ee/components/mobile/authentication/root.tsx +++ b/web/ee/components/mobile/authentication/root.tsx @@ -2,20 +2,21 @@ import { FC, useEffect, useState } from "react"; import { useSearchParams } from "next/navigation"; -import { TInstanceConfig, TMobileWorkspaceInvitation } from "@plane/types"; -// components -import { AuthBanner } from "@/components/account"; -// helpers import { - authErrorHandler, - EAuthenticationErrorCodes, - EAuthSteps, - EAuthModes, - EErrorAlertType, - TAuthErrorInfo, -} from "@/helpers/authentication.helper"; + EMobileAuthSteps, + EMobileAuthModes, + EMobileErrorAlertType, + TMobileAuthSteps, + TMobileAuthModes, + TMobileAuthErrorInfo, + EMobileAuthErrorCodes, + TMobileAuthErrorCodes, +} from "@plane/constants"; +import { TInstanceConfig, TMobileWorkspaceInvitation } from "@plane/types"; +import { mobileAuthErrorHandler } from "@plane/utils"; // plane web components import { + MobileAuthBanner, MobileTermsAndConditions, MobileAuthEmailValidationForm, MobileAuthUniqueCodeForm, @@ -28,23 +29,23 @@ import { import mobileAuthService from "@/plane-web/services/mobile.service"; const UNIQUE_CODE_ERROR_CODES = [ - EAuthenticationErrorCodes.INVALID_MAGIC_CODE_SIGN_IN, - EAuthenticationErrorCodes.INVALID_EMAIL_MAGIC_SIGN_IN, - EAuthenticationErrorCodes.EXPIRED_MAGIC_CODE_SIGN_IN, - EAuthenticationErrorCodes.EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_IN, + EMobileAuthErrorCodes.INVALID_MAGIC_CODE_SIGN_IN, + EMobileAuthErrorCodes.INVALID_EMAIL_MAGIC_SIGN_IN, + EMobileAuthErrorCodes.EXPIRED_MAGIC_CODE_SIGN_IN, + EMobileAuthErrorCodes.EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_IN, ]; -const PASSWORD_ERROR_CODES = [EAuthenticationErrorCodes.AUTHENTICATION_FAILED_SIGN_IN]; +const PASSWORD_ERROR_CODES = [EMobileAuthErrorCodes.AUTHENTICATION_FAILED_SIGN_IN]; // oauth error codes const OAUTH_ERROR_CODES = [ - EAuthenticationErrorCodes.OAUTH_NOT_CONFIGURED, - EAuthenticationErrorCodes.GOOGLE_NOT_CONFIGURED, - EAuthenticationErrorCodes.GITHUB_NOT_CONFIGURED, - EAuthenticationErrorCodes.GOOGLE_OAUTH_PROVIDER_ERROR, - EAuthenticationErrorCodes.GITHUB_OAUTH_PROVIDER_ERROR, - EAuthenticationErrorCodes.GITLAB_OAUTH_PROVIDER_ERROR, - EAuthenticationErrorCodes.MOBILE_SIGNUP_DISABLED, + EMobileAuthErrorCodes.OAUTH_NOT_CONFIGURED, + EMobileAuthErrorCodes.GOOGLE_NOT_CONFIGURED, + EMobileAuthErrorCodes.GITHUB_NOT_CONFIGURED, + EMobileAuthErrorCodes.GOOGLE_OAUTH_PROVIDER_ERROR, + EMobileAuthErrorCodes.GITHUB_OAUTH_PROVIDER_ERROR, + EMobileAuthErrorCodes.GITLAB_OAUTH_PROVIDER_ERROR, + EMobileAuthErrorCodes.MOBILE_SIGNUP_DISABLED, ]; type TAuthRoot = { @@ -62,13 +63,13 @@ export const AuthRoot: FC = (props) => { const errorMessageParam = searchParams.get("error_message"); const sessionToken = searchParams.get("token"); // states - const [authMode, setAuthMode] = useState(EAuthModes.SIGN_IN); + const [authMode, setAuthMode] = useState(EMobileAuthModes.SIGN_IN); const [email, setEmail] = useState(emailParam ? emailParam.toString() : ""); - const [authStep, setAuthStep] = useState(EAuthSteps.EMAIL); - const [errorInfo, setErrorInfo] = useState(undefined); + const [authStep, setAuthStep] = useState(EMobileAuthSteps.EMAIL); + const [errorInfo, setErrorInfo] = useState(undefined); const [invitationDetails, setInvitationDetails] = useState(undefined); - const handleErrorInfo = (value: TAuthErrorInfo | undefined) => { + const handleErrorInfo = (value: TMobileAuthErrorInfo | undefined) => { setErrorInfo(value); }; @@ -83,7 +84,7 @@ export const AuthRoot: FC = (props) => { .generateUniqueCode(payload) .then(() => ({ code: "" })) .catch((error) => { - const errorhandler = authErrorHandler(error?.error_code.toString()); + const errorhandler = mobileAuthErrorHandler(error?.error_code.toString()); if (errorhandler?.type) setErrorInfo(errorhandler); throw error; }); @@ -92,12 +93,12 @@ export const AuthRoot: FC = (props) => { // validating and defining the errors useEffect(() => { if (!errorCodeParam) return; - const errorhandler = authErrorHandler(errorCodeParam?.toString() as EAuthenticationErrorCodes); + const errorhandler = mobileAuthErrorHandler(errorCodeParam?.toString() as TMobileAuthErrorCodes); if (!errorhandler) return; // password handler - if (PASSWORD_ERROR_CODES.includes(errorhandler.code)) setAuthStep(EAuthSteps.PASSWORD); + if (PASSWORD_ERROR_CODES.includes(errorhandler.code)) setAuthStep(EMobileAuthSteps.PASSWORD); // unique code handler - if (UNIQUE_CODE_ERROR_CODES.includes(errorhandler.code)) setAuthStep(EAuthSteps.UNIQUE_CODE); + if (UNIQUE_CODE_ERROR_CODES.includes(errorhandler.code)) setAuthStep(EMobileAuthSteps.UNIQUE_CODE); // oauth signup handler if (OAUTH_ERROR_CODES.includes(errorhandler.code)) setErrorInfo(errorhandler); setErrorInfo(errorhandler); @@ -120,13 +121,13 @@ export const AuthRoot: FC = (props) => { {/* alert banner */} - {errorInfo && errorInfo?.type === EErrorAlertType.BANNER_ALERT && ( - + {errorInfo && errorInfo?.type === EMobileErrorAlertType.BANNER_ALERT && ( + )} {/* auth content */}
- {authStep === EAuthSteps.EMAIL && ( + {authStep === EMobileAuthSteps.EMAIL && ( setEmail(value)} @@ -136,7 +137,7 @@ export const AuthRoot: FC = (props) => { generateEmailUniqueCode={generateEmailUniqueCode} /> )} - {authStep === EAuthSteps.UNIQUE_CODE && ( + {authStep === EMobileAuthSteps.UNIQUE_CODE && ( = (props) => { generateEmailUniqueCode={generateEmailUniqueCode} /> )} - {authStep === EAuthSteps.PASSWORD && ( + {authStep === EMobileAuthSteps.PASSWORD && (