[WEB-4613] chore: mobile auth and minor improvements (#3801)

This commit is contained in:
Anmol Singh Bhatia
2025-08-01 13:45:11 +05:30
committed by GitHub
parent 14297cef00
commit 6c9fff1331
17 changed files with 68 additions and 68 deletions

View File

@@ -68,7 +68,7 @@ export const AuthEmailForm: FC<TAuthEmailForm> = observer((props) => {
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="name@company.com"
className={`disable-autofill-style h-[46px] w-full placeholder:text-onboarding-text-400 autofill:bg-red-500 border-0 focus:bg-none active:bg-transparent`}
className={`disable-autofill-style h-10 w-full placeholder:text-onboarding-text-400 autofill:bg-red-500 border-0 focus:bg-none active:bg-transparent`}
autoComplete="on"
autoFocus
ref={inputRef}
@@ -83,7 +83,7 @@ export const AuthEmailForm: FC<TAuthEmailForm> = observer((props) => {
}}
tabIndex={-1}
>
<XCircle className="h-[46px] w-11 px-3 stroke-custom-text-400 hover:cursor-pointer text-xs" />
<XCircle className="h-10 w-11 px-3 stroke-custom-text-400 hover:cursor-pointer text-xs" />
</button>
)}
</div>

View File

@@ -130,7 +130,7 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
value={passwordFormData.email}
onChange={(e) => handleFormChange("email", e.target.value)}
placeholder="name@company.com"
className={`disable-autofill-style h-[46px] w-full placeholder:text-onboarding-text-400 border-0`}
className={`disable-autofill-style h-10 w-full placeholder:text-onboarding-text-400 border-0`}
disabled
/>
{passwordFormData.email.length > 0 && (
@@ -153,7 +153,7 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
value={passwordFormData.password}
onChange={(e) => handleFormChange("password", e.target.value)}
placeholder="Enter password"
className="disable-autofill-style h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
className="disable-autofill-style h-10 w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
onFocus={() => setIsPasswordInputFocused(true)}
onBlur={() => setIsPasswordInputFocused(false)}
autoComplete="on"
@@ -186,7 +186,7 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
value={passwordFormData.confirm_password}
onChange={(e) => handleFormChange("confirm_password", e.target.value)}
placeholder="Confirm password"
className="disable-autofill-style h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
className="disable-autofill-style h-10 w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
onFocus={() => setIsRetryPasswordInputFocused(true)}
onBlur={() => setIsRetryPasswordInputFocused(false)}
/>

View File

@@ -94,7 +94,7 @@ export const AuthUniqueCodeForm: React.FC<TAuthUniqueCodeForm> = (props) => {
value={uniqueCodeFormData.email}
onChange={(e) => handleFormChange("email", e.target.value)}
placeholder="name@company.com"
className={`disable-autofill-style h-[46px] w-full placeholder:text-onboarding-text-400 border-0`}
className={`disable-autofill-style h-10 w-full placeholder:text-onboarding-text-400 border-0`}
disabled
/>
{uniqueCodeFormData.email.length > 0 && (
@@ -115,7 +115,7 @@ export const AuthUniqueCodeForm: React.FC<TAuthUniqueCodeForm> = (props) => {
value={uniqueCodeFormData.code}
onChange={(e) => handleFormChange("code", e.target.value)}
placeholder="gets-sets-flys"
className="disable-autofill-style h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
className="disable-autofill-style h-10 w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
autoFocus
/>
<div className="flex w-full items-center justify-between px-1 text-xs pt-1">

View File

@@ -2,32 +2,20 @@
import React from "react";
import { observer } from "mobx-react";
import Image from "next/image";
import { useTheme } from "next-themes";
import { TInstanceConfig } from "@plane/types";
// hooks
import { useInstance } from "@/hooks/store";
// components
import { AuthRoot } from "@/plane-web/components/mobile";
// assets
import PlaneBackgroundPatternDark from "@/public/auth/background-pattern-dark.svg";
import PlaneBackgroundPattern from "@/public/auth/background-pattern.svg";
const MobileAuth = observer(() => {
const { resolvedTheme } = useTheme();
// hooks
const { config } = useInstance();
// derived values
const pageBackgroundPattern = resolvedTheme === "dark" ? PlaneBackgroundPatternDark : PlaneBackgroundPattern;
return (
<div className="isolate relative w-screen h-screen overflow-hidden">
<div className="absolute inset-0 z-0">
<Image src={pageBackgroundPattern} className="w-full h-full object-cover" alt="Plane background pattern" />
</div>
<div className="relative z-10 w-screen h-screen overflow-hidden overflow-y-auto flex flex-col">
<div className="py-5 flex flex-col justify-center flex-grow container mx-auto max-w-lg px-10 lg:max-w-md lg:px-5 transition-all">
<div className="isolate relative z-10 flex flex-col items-center w-screen h-screen overflow-hidden overflow-y-auto pt-6 pb-10 px-8">
<div className="flex flex-col justify-center items-center flex-grow w-full py-6 mt-10">
<div className="relative flex flex-col gap-6 max-w-[22.5rem] w-full">
<AuthRoot config={config as TInstanceConfig} />
</div>
</div>

View File

@@ -103,10 +103,10 @@ export const AuthHeader: FC<TAuthHeader> = observer((props) => {
return (
<div className="flex flex-col gap-1">
<span className="text-2xl font-semibold text-custom-text-100">
<span className="text-2xl font-semibold text-custom-text-100 leading-7">
{typeof header === "string" ? t(header) : header}
</span>
<span className="text-2xl font-semibold text-custom-text-400">{subHeader}</span>
<span className="text-2xl font-semibold text-custom-text-400 leading-7">{subHeader}</span>
</div>
);
});

View File

@@ -67,7 +67,7 @@ export const AuthEmailForm: FC<TAuthEmailForm> = observer((props) => {
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder={t("auth.common.email.placeholder")}
className={`disable-autofill-style h-[46px] w-full placeholder:text-custom-text-400 autofill:bg-red-500 border-0 focus:bg-none active:bg-transparent`}
className={`disable-autofill-style h-10 w-full placeholder:text-custom-text-400 autofill:bg-red-500 border-0 focus:bg-none active:bg-transparent`}
autoComplete="on"
autoFocus
ref={inputRef}

View File

@@ -111,7 +111,7 @@ export const ForgotPasswordForm = observer(() => {
ref={ref}
hasError={Boolean(errors.email)}
placeholder={t("auth.common.email.placeholder")}
className="h-[46px] w-full border border-custom-border-300 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
className="h-10 w-full border border-custom-border-300 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
autoComplete="on"
disabled={resendTimerCode > 0}
/>

View File

@@ -195,7 +195,7 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
value={passwordFormData.email}
onChange={(e) => handleFormChange("email", e.target.value)}
placeholder={t("auth.common.email.placeholder")}
className={`disable-autofill-style h-[46px] w-full placeholder:text-custom-text-400 border-0`}
className={`disable-autofill-style h-10 w-full placeholder:text-custom-text-400 border-0`}
disabled
/>
{passwordFormData.email.length > 0 && (
@@ -223,7 +223,7 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
value={passwordFormData.password}
onChange={(e) => handleFormChange("password", e.target.value)}
placeholder={t("auth.common.password.placeholder")}
className="disable-autofill-style h-[46px] w-full border border-custom-border-300 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
className="disable-autofill-style h-10 w-full border border-custom-border-300 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
onFocus={() => setIsPasswordInputFocused(true)}
onBlur={() => setIsPasswordInputFocused(false)}
autoComplete="on"
@@ -260,7 +260,7 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
value={passwordFormData.confirm_password}
onChange={(e) => handleFormChange("confirm_password", e.target.value)}
placeholder={t("auth.common.password.confirm_password.placeholder")}
className="disable-autofill-style h-[46px] w-full border border-custom-border-300 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
className="disable-autofill-style h-10 w-full border border-custom-border-300 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
onFocus={() => setIsRetryPasswordInputFocused(true)}
onBlur={() => setIsRetryPasswordInputFocused(false)}
/>

View File

@@ -117,7 +117,7 @@ export const ResetPasswordForm = observer(() => {
value={resetFormData.email}
//hasError={Boolean(errors.email)}
placeholder={t("auth.common.email.placeholder")}
className="h-[46px] w-full border border-custom-border-300 !bg-custom-background-100 pr-12 text-custom-text-400 cursor-not-allowed"
className="h-10 w-full border border-custom-border-300 !bg-custom-background-100 pr-12 text-custom-text-400 cursor-not-allowed"
autoComplete="on"
disabled
/>
@@ -135,7 +135,7 @@ export const ResetPasswordForm = observer(() => {
onChange={(e) => handleFormChange("password", e.target.value)}
//hasError={Boolean(errors.password)}
placeholder={t("auth.common.password.placeholder")}
className="h-[46px] w-full border border-custom-border-300 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
className="h-10 w-full border border-custom-border-300 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
minLength={8}
onFocus={() => setIsPasswordInputFocused(true)}
onBlur={() => setIsPasswordInputFocused(false)}
@@ -167,7 +167,7 @@ export const ResetPasswordForm = observer(() => {
value={resetFormData.confirm_password}
onChange={(e) => handleFormChange("confirm_password", e.target.value)}
placeholder={t("auth.common.password.confirm_password.placeholder")}
className="h-[46px] w-full border border-custom-border-300 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
className="h-10 w-full border border-custom-border-300 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
onFocus={() => setIsRetryPasswordInputFocused(true)}
onBlur={() => setIsRetryPasswordInputFocused(false)}
/>

View File

@@ -131,7 +131,7 @@ export const SetPasswordForm = observer(() => {
value={user?.email}
//hasError={Boolean(errors.email)}
placeholder={t("auth.common.email.placeholder")}
className="h-[46px] w-full border border-custom-border-300 !bg-custom-background-100 pr-12 text-custom-text-400 cursor-not-allowed"
className="h-10 w-full border border-custom-border-300 !bg-custom-background-100 pr-12 text-custom-text-400 cursor-not-allowed"
autoComplete="on"
disabled
/>
@@ -149,7 +149,7 @@ export const SetPasswordForm = observer(() => {
onChange={(e) => handleFormChange("password", e.target.value)}
//hasError={Boolean(errors.password)}
placeholder={t("auth.common.password.placeholder")}
className="h-[46px] w-full border border-custom-border-300 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
className="h-10 w-full border border-custom-border-300 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
minLength={8}
onFocus={() => setIsPasswordInputFocused(true)}
onBlur={() => setIsPasswordInputFocused(false)}
@@ -181,7 +181,7 @@ export const SetPasswordForm = observer(() => {
value={passwordFormData.confirm_password}
onChange={(e) => handleFormChange("confirm_password", e.target.value)}
placeholder={t("auth.common.password.confirm_password.placeholder")}
className="h-[46px] w-full border border-custom-border-300 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
className="h-10 w-full border border-custom-border-300 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
onFocus={() => setIsRetryPasswordInputFocused(true)}
onBlur={() => setIsRetryPasswordInputFocused(false)}
/>

View File

@@ -129,7 +129,7 @@ export const AuthUniqueCodeForm: React.FC<TAuthUniqueCodeForm> = (props) => {
value={uniqueCodeFormData.email}
onChange={(e) => handleFormChange("email", e.target.value)}
placeholder={t("auth.common.email.placeholder")}
className="disable-autofill-style h-[46px] w-full placeholder:text-custom-text-400 border-0"
className="disable-autofill-style h-10 w-full placeholder:text-custom-text-400 border-0"
autoComplete="on"
disabled
/>
@@ -156,7 +156,7 @@ export const AuthUniqueCodeForm: React.FC<TAuthUniqueCodeForm> = (props) => {
value={uniqueCodeFormData.code}
onChange={(e) => handleFormChange("code", e.target.value)}
placeholder={t("auth.common.unique_code.placeholder")}
className="disable-autofill-style h-[46px] w-full border border-custom-border-300 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
className="disable-autofill-style h-10 w-full border border-custom-border-300 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
autoFocus
/>
<div className="flex w-full items-center justify-between px-1 text-xs pt-1">

View File

@@ -26,7 +26,7 @@ const BRAND_LOGOS: {
export const AuthFooter = () => (
<div className="flex flex-col items-center gap-6">
<span className="text-md text-custom-text-300 whitespace-nowrap">Join 10,000+ teams building with Plane</span>
<span className="text-sm text-custom-text-300 whitespace-nowrap">Join 10,000+ teams building with Plane</span>
<div className="flex items-center justify-center gap-x-10 gap-y-4 w-full flex-wrap">
{BRAND_LOGOS.map((brand) => (
<div className="flex items-center justify-center h-7 flex-1" key={brand.id}>

View File

@@ -135,14 +135,14 @@ export const MobileAuthEmailValidationForm: FC<TMobileAuthEmailValidationForm> =
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="name@example.com"
className={`disable-autofill-style h-[46px] w-full placeholder:text-custom-text-400 autofill:bg-red-500 border-0 focus:bg-none active:bg-transparent`}
className={`disable-autofill-style h-10 w-full placeholder:text-custom-text-400 autofill:bg-red-500 border-0 focus:bg-none active:bg-transparent`}
autoComplete="on"
autoFocus
ref={inputRef}
/>
{email.length > 0 && (
<XCircle
className="h-[46px] w-11 px-3 stroke-custom-text-400 hover:cursor-pointer text-xs"
className="h-10 w-11 px-3 stroke-custom-text-400 hover:cursor-pointer text-xs"
onClick={() => {
setEmail("");
inputRef.current?.focus();

View File

@@ -156,7 +156,7 @@ export const MobileAuthPasswordForm: FC<TMobileAuthPasswordForm> = (props) => {
value={formData.email}
onChange={(e) => handleFormChange("email", e.target.value)}
placeholder="name@company.com"
className={`disable-autofill-style h-[46px] w-full placeholder:text-custom-text-400 border-0`}
className={`disable-autofill-style h-10 w-full placeholder:text-custom-text-400 border-0`}
disabled
/>
{formData.email.length > 0 && (
@@ -180,7 +180,7 @@ export const MobileAuthPasswordForm: FC<TMobileAuthPasswordForm> = (props) => {
value={formData.password}
onChange={(e) => handleFormChange("password", e.target.value)}
placeholder="Enter password"
className="disable-autofill-style h-[46px] w-full border border-custom-border-100 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
className="disable-autofill-style h-10 w-full border border-custom-border-100 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
autoComplete="on"
autoFocus
onFocus={() => handlePasswordInputFocused("password")}
@@ -218,7 +218,7 @@ export const MobileAuthPasswordForm: FC<TMobileAuthPasswordForm> = (props) => {
value={formData.passwordConfirmation}
onChange={(e) => handleFormChange("passwordConfirmation", e.target.value)}
placeholder="Enter password"
className="disable-autofill-style h-[46px] w-full border border-custom-border-100 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
className="disable-autofill-style h-10 w-full border border-custom-border-100 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
disabled={!isPasswordConfirmationEnabled}
onFocus={() => handlePasswordInputFocused("passwordConfirmation")}
onBlur={() => handlePasswordInputFocused("passwordConfirmation")}

View File

@@ -114,7 +114,7 @@ export const MobileAuthUniqueCodeForm: FC<TMobileAuthUniqueCodeForm> = (props) =
value={formData.email}
onChange={(e) => handleFormChange("email", e.target.value)}
placeholder="name@company.com"
className={`disable-autofill-style h-[46px] w-full placeholder:text-custom-text-400 border-0`}
className={`disable-autofill-style h-10 w-full placeholder:text-custom-text-400 border-0`}
disabled
/>
{formData.email.length > 0 && (
@@ -137,7 +137,7 @@ export const MobileAuthUniqueCodeForm: FC<TMobileAuthUniqueCodeForm> = (props) =
value={formData.code}
onChange={(e) => handleFormChange("code", e.target.value)}
placeholder="gets-sets-flys"
className="disable-autofill-style h-[46px] w-full border border-custom-border-100 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
className="disable-autofill-style h-10 w-full border border-custom-border-100 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
autoComplete="on"
autoFocus
/>

View File

@@ -1,11 +1,9 @@
"use client";
import { FC } from "react";
import Image from "next/image";
import { EMobileAuthModes, EMobileAuthSteps, TMobileAuthModes, TMobileAuthSteps } from "@plane/constants";
import { TMobileWorkspaceInvitation } from "@plane/types";
// assets
import planeLogo from "@/public/plane-logos/blue-without-text.png";
import { PlaneLogo } from "@plane/ui";
const AUTH_SIGNUP_HEADER_CONTENT_OPTIONS = {
[EMobileAuthSteps.EMAIL]: {
@@ -69,11 +67,9 @@ export const MobileAuthHeader: FC<TMobileAuthHeader> = (props) => {
return (
<div className="relative space-y-6">
<div className="relative h-[50px] w-[50px]">
<Image src={planeLogo} alt="Plane logo" className="object-contain" />
</div>
<div className="space-y-1">
<h3 className="text-3xl font-semibold text-custom-text-100">{title}</h3>
<PlaneLogo height={51} width={84} className="text-custom-text-100" />
<div className="flex flex-col gap-1">
<h3 className="text-2xl font-semibold text-custom-text-100">{title}</h3>
{description && <p className="font-medium text-custom-text-400">{description}</p>}
</div>
</div>

View File

@@ -1,21 +1,37 @@
"use client";
import { FC } from "react";
import React from "react";
import Link from "next/link";
import { EAuthModes } from "@plane/constants";
export const MobileTermsAndConditions: FC = () => (
<div className="flex items-center justify-center py-4">
<div className="text-center text-sm text-custom-text-200 whitespace-pre-line">
By signing in, you agree to our
<br />
<Link href="https://plane.so/legals/terms-and-conditions" target="_blank" rel="noopener noreferrer">
<span className="text-sm font-medium underline hover:cursor-pointer">Terms of Service</span>
</Link>
&nbsp;and&nbsp;
<Link href="https://plane.so/legals/privacy-policy" target="_blank" rel="noopener noreferrer">
<span className="text-sm font-medium underline hover:cursor-pointer">Privacy Policy</span>
</Link>
.
</div>
interface TermsAndConditionsProps {
authType?: EAuthModes;
}
// Constants for better maintainability
const LEGAL_LINKS = {
termsOfService: "https://plane.so/legals/terms-and-conditions",
privacyPolicy: "https://plane.so/legals/privacy-policy",
} as const;
const MESSAGES = {
[EAuthModes.SIGN_UP]: "By creating an account",
[EAuthModes.SIGN_IN]: "By signing in",
} as const;
// Reusable link component to reduce duplication
const LegalLink: React.FC<{ href: string; children: React.ReactNode }> = ({ href, children }) => (
<Link href={href} className="text-custom-text-200" target="_blank" rel="noopener noreferrer">
<span className="text-sm font-medium underline hover:cursor-pointer">{children}</span>
</Link>
);
export const MobileTermsAndConditions: React.FC<TermsAndConditionsProps> = ({ authType = EAuthModes.SIGN_IN }) => (
<div className="flex items-center justify-center">
<p className="text-center text-sm text-custom-text-300 whitespace-pre-line">
{`${MESSAGES[authType]}, you understand and agree to \n our `}
<LegalLink href={LEGAL_LINKS.termsOfService}>Terms of Service</LegalLink> and{" "}
<LegalLink href={LEGAL_LINKS.privacyPolicy}>Privacy Policy</LegalLink>.
</p>
</div>
);