refactor: login with magic link
This commit is contained in:
@@ -114,7 +114,7 @@ const PageRoutes = () => {
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/send_magic_link"
|
||||
path="/send_magic_link/:email"
|
||||
element={
|
||||
<LazyIt>
|
||||
<RequireNoAuth>
|
||||
|
||||
@@ -2,15 +2,18 @@ import { useNavigate } from "react-router-dom";
|
||||
|
||||
import Button from "@/components/styled/Button";
|
||||
|
||||
export default function MagicLinkLogin() {
|
||||
type Props = {
|
||||
email: string;
|
||||
};
|
||||
export default function MagicLinkLogin({ email }: Props) {
|
||||
const navigate = useNavigate();
|
||||
const handleLogin = () => {
|
||||
navigate("/send_magic_link");
|
||||
navigate(`/send_magic_link/${email}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<Button className="w-full" onClick={handleLogin}>
|
||||
Sign in with Magic Link
|
||||
<Button className="w-full ghost" onClick={handleLogin}>
|
||||
Sign in with a Magic Link
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
+49
-27
@@ -11,6 +11,7 @@ import { useAppSelector } from "@/app/store";
|
||||
import Divider from "@/components/Divider";
|
||||
import Button from "@/components/styled/Button";
|
||||
import Input from "@/components/styled/Input";
|
||||
import StyledLabel from "@/components/styled/Label";
|
||||
import MagicLinkLogin from "./MagicLinkLogin";
|
||||
import SignUpLink from "./SignUpLink";
|
||||
import SocialLoginButtons from "./SocialLoginButtons";
|
||||
@@ -22,6 +23,7 @@ export default function LoginPage() {
|
||||
const { data: enableSMTP, isLoading: loadingSMTPStatus } = useGetSMTPStatusQuery();
|
||||
const [login, { isSuccess, isLoading, error }] = useLoginMutation();
|
||||
const { data: loginConfig, isSuccess: loginConfigSuccess } = useGetLoginConfigQuery();
|
||||
const [emailInputted, setEmailInputted] = useState(false);
|
||||
const [input, setInput] = useState({
|
||||
email: "",
|
||||
password: ""
|
||||
@@ -90,6 +92,11 @@ export default function LoginPage() {
|
||||
|
||||
const handleLogin = (evt: FormEvent<HTMLFormElement>) => {
|
||||
evt.preventDefault();
|
||||
const enableMagicLink = enableSMTP && loginConfig?.magic_link;
|
||||
if (enableMagicLink && !emailInputted) {
|
||||
setEmailInputted(true);
|
||||
return;
|
||||
}
|
||||
login({
|
||||
...input,
|
||||
type: "password"
|
||||
@@ -111,7 +118,8 @@ export default function LoginPage() {
|
||||
const { magic_link, who_can_sign_up: whoCanSignUp } = loginConfig;
|
||||
|
||||
const enableMagicLink = enableSMTP && magic_link;
|
||||
|
||||
const hideSocials = enableMagicLink && emailInputted;
|
||||
const showSignIn = !enableMagicLink || emailInputted;
|
||||
if (loadingSMTPStatus) return null;
|
||||
return (
|
||||
<div className="flex-center h-screen dark:bg-gray-700">
|
||||
@@ -127,36 +135,50 @@ export default function LoginPage() {
|
||||
</h2>
|
||||
</div>
|
||||
<form className="flex flex-col gap-5 w-80 md:min-w-[360px] " onSubmit={handleLogin}>
|
||||
<Input
|
||||
className="large"
|
||||
name="email"
|
||||
value={email}
|
||||
type="email"
|
||||
required
|
||||
placeholder={t("placeholder_email")}
|
||||
data-type="email"
|
||||
onChange={handleInput}
|
||||
/>
|
||||
<Input
|
||||
className="large"
|
||||
type="password"
|
||||
value={password}
|
||||
name="password"
|
||||
required
|
||||
data-type="password"
|
||||
onChange={handleInput}
|
||||
placeholder={t("placeholder_pwd")}
|
||||
/>
|
||||
<Button type="submit" disabled={isLoading}>
|
||||
{isLoading ? "Signing" : t("sign_in")}
|
||||
</Button>
|
||||
{!emailInputted && (
|
||||
<div className="flex flex-col gap-1">
|
||||
<StyledLabel>Email</StyledLabel>
|
||||
<Input
|
||||
className="large"
|
||||
name="email"
|
||||
value={email}
|
||||
type="email"
|
||||
required
|
||||
placeholder={t("placeholder_email")}
|
||||
data-type="email"
|
||||
onChange={handleInput}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{(!enableMagicLink || emailInputted) && (
|
||||
<div className="">
|
||||
<StyledLabel>Password</StyledLabel>
|
||||
<Input
|
||||
className="large"
|
||||
type="password"
|
||||
value={password}
|
||||
name="password"
|
||||
required
|
||||
data-type="password"
|
||||
onChange={handleInput}
|
||||
placeholder={t("placeholder_pwd")}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{showSignIn ? (
|
||||
<Button type="submit" disabled={isLoading}>
|
||||
{isLoading ? "Signing" : t("sign_in")}
|
||||
</Button>
|
||||
) : (
|
||||
<Button type="submit">{t("continue")}</Button>
|
||||
)}
|
||||
</form>
|
||||
<Divider content="OR" />
|
||||
<div className="socials flex flex-col gap-3">
|
||||
{enableMagicLink && <MagicLinkLogin />}
|
||||
<SocialLoginButtons />
|
||||
{emailInputted && <MagicLinkLogin email={input.email} />}
|
||||
{!hideSocials && <SocialLoginButtons />}
|
||||
</div>
|
||||
{whoCanSignUp === "EveryOne" && <SignUpLink />}
|
||||
{whoCanSignUp === "EveryOne" && !hideSocials && <SignUpLink />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,23 +1,33 @@
|
||||
import { FC, MouseEvent } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
|
||||
import BASE_URL from "@/app/config";
|
||||
import Button from "@/components/styled/Button";
|
||||
|
||||
interface Props {
|
||||
email: string;
|
||||
reset?: (e: MouseEvent<HTMLButtonElement>) => void;
|
||||
handleBack: () => void;
|
||||
}
|
||||
|
||||
const SentTip: FC<Props> = ({ email, reset }) => {
|
||||
const SentTip: FC<Props> = ({ email, handleBack }) => {
|
||||
const { t } = useTranslation("auth");
|
||||
return (
|
||||
<div className="flex flex-col items-center">
|
||||
<h2 className="font-semibold text-2xl text-gray-800 dark:text-gray-200 mb-2">
|
||||
<div className="flex flex-col items-center max-w-sm">
|
||||
<img
|
||||
src={`${BASE_URL}/resource/organization/logo`}
|
||||
alt="logo"
|
||||
className="w-14 h-14 mb-2 md:mb-7 rounded-full"
|
||||
/>
|
||||
<h2 className="font-semibold text-2xl text-gray-900 dark:text-gray-100 mb-2">
|
||||
{t("check_email")}
|
||||
</h2>
|
||||
<span className="text-center text-gray-500 mb-6">{t("check_email_desc", { email })}</span>
|
||||
<Button onClick={reset} className="main flex-center">
|
||||
{t("use_different")}
|
||||
<span className="text-center text-gray-500 mb-6">
|
||||
<Trans i18nKey={"check_email_desc"} ns="auth" values={{ email }}>
|
||||
<strong className="font-bold" />
|
||||
</Trans>
|
||||
</span>
|
||||
<Button onClick={handleBack} className="flex-center ghost">
|
||||
{t("back_sign_in")}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,30 +1,23 @@
|
||||
import { ChangeEvent, FormEvent, useEffect, useState } from "react";
|
||||
import { useEffect } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
|
||||
import BASE_URL from "@/app/config";
|
||||
import { useSendLoginMagicLinkMutation } from "@/app/services/auth";
|
||||
import { useAppSelector } from "@/app/store";
|
||||
import Divider from "@/components/Divider";
|
||||
import Button from "@/components/styled/Button";
|
||||
import Input from "@/components/styled/Input";
|
||||
import SocialLoginButtons from "../login/SocialLoginButtons";
|
||||
import SignInLink from "../reg/SignInLink";
|
||||
import SentTip from "./SentTip";
|
||||
|
||||
export default function SendMagicLinkPage() {
|
||||
const { t } = useTranslation("auth");
|
||||
const serverName = useAppSelector((store) => store.server.name);
|
||||
const [sent, setSent] = useState(false);
|
||||
const { email = "" } = useParams();
|
||||
const [sendMagicLink, { isSuccess, isLoading, error }] = useSendLoginMagicLinkMutation();
|
||||
const navigateTo = useNavigate();
|
||||
const [email, setEmail] = useState("");
|
||||
useEffect(() => {
|
||||
if (email) {
|
||||
sendMagicLink(email);
|
||||
}
|
||||
}, [email]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
toast.success("Send Email Successfully!");
|
||||
setSent(true);
|
||||
}
|
||||
}, [isSuccess]);
|
||||
|
||||
@@ -52,64 +45,14 @@ export default function SendMagicLinkPage() {
|
||||
navigateTo("/login");
|
||||
};
|
||||
|
||||
const handleLogin = (evt: FormEvent<HTMLFormElement>) => {
|
||||
evt.preventDefault();
|
||||
sendMagicLink(email);
|
||||
};
|
||||
|
||||
const handleInput = (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
const { value } = evt.target;
|
||||
setEmail(value);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setEmail("");
|
||||
setSent(false);
|
||||
};
|
||||
return (
|
||||
<div className="flex-center h-screen dark:bg-gray-700">
|
||||
<div className="py-8 px-10 shadow rounded-xl">
|
||||
{sent ? (
|
||||
<SentTip email={email} reset={handleReset} />
|
||||
) : (
|
||||
<>
|
||||
<div className="flex flex-col items-center">
|
||||
<img
|
||||
src={`${BASE_URL}/resource/organization/logo`}
|
||||
alt="logo"
|
||||
className="w-14 h-14 mb-7 rounded-full"
|
||||
/>
|
||||
<h2 className="font-semibold text-2xl text-gray-800 dark:text-gray-200 mb-2">
|
||||
{t("enter")} {serverName}{" "}
|
||||
</h2>
|
||||
<span className="text-center text-gray-500 mb-6">{t("placeholder_email")}</span>
|
||||
</div>
|
||||
<form onSubmit={handleLogin} className="flex flex-col gap-5 w-[360px]">
|
||||
<Input
|
||||
type="email"
|
||||
className="large"
|
||||
name="email"
|
||||
autoFocus
|
||||
value={email}
|
||||
required
|
||||
// pattern={`^\S+@\S+\.\S+$`}
|
||||
placeholder={t("placeholder_email")}
|
||||
onChange={handleInput}
|
||||
/>
|
||||
<Button type="submit" disabled={isLoading || !email}>
|
||||
{isLoading ? "Sending" : t("continue")}
|
||||
</Button>
|
||||
</form>
|
||||
<Divider content="OR" />
|
||||
<Button onClick={handlePwdPath} className="w-full">
|
||||
{t("login.password")}
|
||||
</Button>
|
||||
<div className="flex flex-col gap-3 py-3 empty:hidden">
|
||||
<SocialLoginButtons />
|
||||
</div>
|
||||
<SignInLink />
|
||||
</>
|
||||
)}
|
||||
<div className="flex-center h-screen">
|
||||
<div className="py-8 px-10 shadow-md rounded-xl bg-white dark:bg-gray-700">
|
||||
{isSuccess ? (
|
||||
<SentTip email={email} handleBack={handlePwdPath} />
|
||||
) : isLoading ? (
|
||||
<div className="">Sending...</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user