Files
upstream-mirror 23059a1de3
Some checks failed
CodeQL / Analyze (javascript) (push) Failing after 16s
CodeQL / Analyze (python) (push) Failing after 3s
i18n sync check / check:sync (push) Failing after 3s
React Doctor / react-doctor (push) Failing after 16s
Branch Build CE / Build-Push Admin Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge Admin Manifest (push) Has been cancelled
Branch Build CE / Build-Push Web Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge Web Manifest (push) Has been cancelled
Branch Build CE / Build-Push Space Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge Proxy Manifest (push) Has been cancelled
Branch Build CE / Merge Space Manifest (push) Has been cancelled
Branch Build CE / Prepare AIO Assets (push) Has been cancelled
Branch Build CE / Build Setup (push) Has been cancelled
Branch Build CE / Build-Push Live Collaboration Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge Live Collaboration Manifest (push) Has been cancelled
Branch Build CE / Build-Push API Server Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge API Server Manifest (push) Has been cancelled
Branch Build CE / Build-Push Proxy Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Build-Push AIO Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge AIO Manifest (push) Has been cancelled
Branch Build CE / Upload Build Assets (push) Has been cancelled
Branch Build CE / Build Release (push) Has been cancelled
snapshot of https://github.com/makeplane/plane.git @ d868349a55de79979eb8b98701bf1702c67ad634 (2026-09-15)
2026-09-15 15:14:58 +05:00

109 lines
3.5 KiB
TypeScript

/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import type { FormEvent } from "react";
import { useMemo, useRef, useState } from "react";
import { observer } from "mobx-react";
// icons
import { CloseCircleOutline, WarningCircleOutline } from "@makeplane/propel/icons";
// types
import { Button } from "@plane/propel/button";
import type { IEmailCheckData } from "@plane/types";
// ui
import { Input, Spinner } from "@plane/ui";
// helpers
import { cn } from "@plane/utils";
import { checkEmailValidity } from "@/helpers/string.helper";
type TAuthEmailForm = {
defaultEmail: string;
onSubmit: (data: IEmailCheckData) => Promise<void>;
};
export const AuthEmailForm = observer(function AuthEmailForm(props: TAuthEmailForm) {
const { onSubmit, defaultEmail } = props;
// states
const [isSubmitting, setIsSubmitting] = useState(false);
const [email, setEmail] = useState(defaultEmail);
const emailError = useMemo(
() => (email && !checkEmailValidity(email) ? { email: "Email is invalid" } : undefined),
[email]
);
const handleFormSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
setIsSubmitting(true);
const payload: IEmailCheckData = {
email: email,
};
await onSubmit(payload);
setIsSubmitting(false);
};
const isButtonDisabled = email.length === 0 || Boolean(emailError?.email) || isSubmitting;
const [isFocused, setIsFocused] = useState(true);
const inputRef = useRef<HTMLInputElement>(null);
return (
<form onSubmit={handleFormSubmit} className="mt-5 space-y-4">
<div className="space-y-1">
<label className="text-13 font-medium text-tertiary" htmlFor="email">
Email
</label>
<div
className={cn(
`relative flex items-center rounded-md border bg-surface-1`,
!isFocused && Boolean(emailError?.email) ? `border-danger-strong` : `border-subtle`
)}
onFocus={() => {
setIsFocused(true);
}}
onBlur={() => {
setIsFocused(false);
}}
>
<Input
id="email"
name="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="name@company.com"
className={`h-10 w-full border-0 disable-autofill-style placeholder:text-placeholder autofill:bg-danger-subtle focus:bg-none active:bg-transparent`}
autoComplete="off"
autoFocus
ref={inputRef}
/>
{email.length > 0 && (
<button
type="button"
aria-label="Clear email"
onClick={() => {
setEmail("");
inputRef.current?.focus();
}}
tabIndex={-1}
>
<CloseCircleOutline className="h-10 w-11 px-3 text-11 text-placeholder hover:cursor-pointer" />
</button>
)}
</div>
{emailError?.email && !isFocused && (
<p className="flex items-center gap-1 px-0.5 text-11 text-danger-primary">
<WarningCircleOutline height={12} width={12} />
{emailError.email}
</p>
)}
</div>
<Button type="submit" variant="primary" className="w-full" size="xl" disabled={isButtonDisabled}>
{isSubmitting ? <Spinner height="20px" width="20px" /> : "Continue"}
</Button>
</form>
);
});