Add sign in function
This commit is contained in:
1 parent
38b869084d
commit
eb81d45008
10 files changed
+320
-52
No files matched your search
@@ -3,8 +3,11 @@ import sign_in_icon from "./icons/sign-in_icon.svg";
|
||||
import { Link } from "@/shared/config/navigation";
|
||||
import { Container } from "@/shared/ui";
|
||||
import SignInForm from "@/widgets/forms/SignInForm";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
const SignIn = () => {
|
||||
const t = useTranslations("signIn");
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<div className="h-full min-h-[800px] w-full flex justify-center">
|
||||
@@ -14,19 +17,19 @@ const SignIn = () => {
|
||||
</div>
|
||||
<div className="mb-2 flex flex-col items-center gap-2 text-center">
|
||||
<h2 className="text-[24px] font-bold leading-8 text-gray-900">
|
||||
Войдите в аккаунт
|
||||
{t("login")}
|
||||
</h2>
|
||||
<p className="text-gray-500">Пожалуйста, введите свои данные</p>
|
||||
<p className="text-gray-500">{t("info")}</p>
|
||||
</div>
|
||||
|
||||
<SignInForm />
|
||||
<p className="text-[14px] font-semibold leading-5 text-gray-500">
|
||||
Еще нет аккаунта?{" "}
|
||||
{t("noAccount")}{" "}
|
||||
<Link
|
||||
className="text-[14px] font-semibold leading-5 text-light-blue"
|
||||
href="/sign-up"
|
||||
>
|
||||
Зарегистрируйтесь
|
||||
{t("register")}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import NextAuth from "next-auth";
|
||||
import { authConfig } from "@/shared/config/authConfig";
|
||||
|
||||
const handler = NextAuth(authConfig);
|
||||
|
||||
export { handler as GET, handler as POST };
|
||||
@@ -0,0 +1,111 @@
|
||||
import { AuthOptions } from "next-auth";
|
||||
import { JWT } from "next-auth/jwt";
|
||||
import CredentialsProvider from "next-auth/providers/credentials";
|
||||
import { apiInstance } from "./apiConfig";
|
||||
import { IRefresh, ITokens } from "../types/token-type";
|
||||
|
||||
const refreshToken = async (token: JWT): Promise<JWT> => {
|
||||
const UTC = new Date();
|
||||
|
||||
const data = {
|
||||
refresh: token.refresh_token,
|
||||
};
|
||||
|
||||
const response = await apiInstance.post<IRefresh>(
|
||||
"/auth/token/refresh/",
|
||||
data
|
||||
);
|
||||
|
||||
const expirationTime = new Date(UTC.getTime() + 14 * 60000);
|
||||
expirationTime.setTime(
|
||||
expirationTime.getTime() +
|
||||
expirationTime.getTimezoneOffset() * 60 * 1000 * -1
|
||||
);
|
||||
|
||||
return {
|
||||
...token,
|
||||
access_token: response.data.access,
|
||||
expires_in: expirationTime,
|
||||
};
|
||||
};
|
||||
|
||||
export const authConfig: AuthOptions = {
|
||||
providers: [
|
||||
CredentialsProvider({
|
||||
name: "Credentials",
|
||||
credentials: {
|
||||
email: {
|
||||
label: "Email",
|
||||
type: "text",
|
||||
placeholder: "jsmith@example.com",
|
||||
},
|
||||
password: { label: "Password", type: "password" },
|
||||
},
|
||||
async authorize(credentials, req): Promise<any> {
|
||||
if (!credentials?.email || !credentials?.password) return null;
|
||||
|
||||
const { email, password } = credentials;
|
||||
const data = {
|
||||
email,
|
||||
password,
|
||||
};
|
||||
|
||||
const res = await apiInstance.post<ITokens>("/auth/login/", data);
|
||||
|
||||
if ([200, 201].includes(res.status)) {
|
||||
const currentTime = new Date();
|
||||
const expirationTime = new Date(currentTime.getTime() + 14 * 60000);
|
||||
expirationTime.setTime(
|
||||
expirationTime.getTime() +
|
||||
expirationTime.getTimezoneOffset() * 60 * 1000 * -1
|
||||
);
|
||||
|
||||
const user = {
|
||||
refresh_token: res.data.refresh_token,
|
||||
access_token: res.data.access_token,
|
||||
expires_in: expirationTime,
|
||||
};
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
}),
|
||||
],
|
||||
pages: {
|
||||
signIn: "/sign-in",
|
||||
},
|
||||
session: {
|
||||
strategy: "jwt",
|
||||
},
|
||||
callbacks: {
|
||||
async signIn({ account, user }) {
|
||||
return true;
|
||||
},
|
||||
async jwt({ token, user }) {
|
||||
if (user) return { ...token, ...user };
|
||||
|
||||
const UTC = new Date();
|
||||
const currentTime = new Date(UTC.getTime());
|
||||
currentTime.setTime(
|
||||
currentTime.getTime() + currentTime.getTimezoneOffset() * 60 * 1000 * -1
|
||||
);
|
||||
|
||||
const isValid =
|
||||
new Date(currentTime).getTime() <= new Date(token.expires_in).getTime();
|
||||
|
||||
if (isValid) return token;
|
||||
|
||||
return await refreshToken(token);
|
||||
},
|
||||
|
||||
async session({ token, session }) {
|
||||
session.access_token = token.access_token;
|
||||
session.refresh_token = token.refresh_token;
|
||||
session.expires_in = token.expires_in;
|
||||
|
||||
return session;
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
export interface ITokens {
|
||||
refresh_token: string;
|
||||
access_token: string;
|
||||
expires_in: string;
|
||||
}
|
||||
|
||||
export interface IRefresh {
|
||||
access: string;
|
||||
expires_at: string;
|
||||
}
|
||||
@@ -38,13 +38,12 @@ const ConfirmEmailForm: React.FC<IConfirmEmailFormProps> = ({
|
||||
e.preventDefault();
|
||||
|
||||
const codeString = otp.join("");
|
||||
const codeNumber = parseInt(codeString);
|
||||
console.log(parseInt("01"));
|
||||
|
||||
try {
|
||||
setLoader(true);
|
||||
console.log(codeNumber);
|
||||
|
||||
const response = await apiInstance.post("/auth/confirm_user/", {
|
||||
code: codeNumber,
|
||||
code: codeString,
|
||||
});
|
||||
console.log(response);
|
||||
if (response.status === 200 || response.status === 201) {
|
||||
|
||||
@@ -1,18 +1,28 @@
|
||||
"use client";
|
||||
import React, { FC, useState } from "react";
|
||||
import React from "react";
|
||||
import { z } from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import AuthInput from "@/features/AuthInput";
|
||||
import Loader from "@/shared/ui/Loader/Loader";
|
||||
import Link from "next/link";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useRouter } from "@/shared/config/navigation";
|
||||
import alert from "./icons/alert-circle.svg";
|
||||
import Image from "next/image";
|
||||
import eye_off from "./icons/eye-off.svg";
|
||||
import eye_on from "./icons/eye-on.svg";
|
||||
import Loader from "@/shared/ui/Loader/Loader";
|
||||
import { apiInstance } from "@/shared/config/apiConfig";
|
||||
import { signIn, useSession } from "next-auth/react";
|
||||
import { AxiosError } from "axios";
|
||||
|
||||
// interface SignInProps {
|
||||
// isLoading: boolean;
|
||||
// onSubmitFunc: () => void;
|
||||
// noAccountError: Error | null;
|
||||
// }
|
||||
const SignInForm = ({}) => {
|
||||
const session = useSession();
|
||||
const t = useTranslations("signIn");
|
||||
const router = useRouter();
|
||||
const [showPassword, setShowPassword] = React.useState(false);
|
||||
const [loader, setLoader] = React.useState<boolean>(false);
|
||||
const [error, setError] = React.useState<string>("");
|
||||
|
||||
const signInFormScheme = z.object({
|
||||
email: z.string().email("Неверный формат email"),
|
||||
password: z.string().min(8, "Пароль должен содержать минимум 8 символов"),
|
||||
@@ -23,46 +33,121 @@ const SignInForm = ({}) => {
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
formState: { errors, isSubmitting },
|
||||
} = useForm<FormFields>({
|
||||
resolver: zodResolver(signInFormScheme),
|
||||
});
|
||||
|
||||
const onSubmit = (data: FormFields) => {};
|
||||
// const onSubmit = async (data: FormFields) => {
|
||||
// try {
|
||||
// console.log(data);
|
||||
// const res = await apiInstance.post("/auth/login/", data);
|
||||
// if ([200, 201].includes(res.status)) {
|
||||
// router.push("/");
|
||||
// }
|
||||
// } catch (error: unknown) {
|
||||
// if (error instanceof AxiosError) {
|
||||
// if ([400, 401, 404].includes(error.response?.status as number)) {
|
||||
// setError("Неверный Email или Пароль");
|
||||
// } else if (error.response?.status.toString().slice(0, 1) === "5") {
|
||||
// setError("Ошибка на стороне сервера");
|
||||
// }
|
||||
// } else {
|
||||
// setError("Непредвиденная ошибка");
|
||||
// }
|
||||
// } finally {
|
||||
// setLoader(false);
|
||||
// }
|
||||
// };
|
||||
const onSubmit = async (data: FormFields) => {
|
||||
setLoader(true);
|
||||
const result = await signIn("credentials", {
|
||||
redirect: false,
|
||||
email: data.email,
|
||||
password: data.password,
|
||||
});
|
||||
|
||||
if (result?.error) {
|
||||
setError(result.error);
|
||||
} else if (result?.ok) {
|
||||
router.push("/");
|
||||
}
|
||||
|
||||
setLoader(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<form className="mb-2 w-[360px] flex flex-col">
|
||||
<form
|
||||
className="mb-2 w-[360px] flex flex-col"
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
>
|
||||
<div className="flex flex-col gap-5">
|
||||
<AuthInput
|
||||
type="email"
|
||||
label="Email"
|
||||
placeholder="Введите email"
|
||||
error={errors.email?.message}
|
||||
{...register("email", { required: true })}
|
||||
/>
|
||||
<AuthInput
|
||||
isPassword
|
||||
label="Пароль"
|
||||
placeholder="Введите пароль"
|
||||
error={errors.password?.message}
|
||||
{...register("password", { required: true })}
|
||||
/>
|
||||
<div>
|
||||
<label className="text-[14px] leading-5 text-gray-700">Email</label>
|
||||
<div
|
||||
className={`flex items-center${
|
||||
errors.email?.message && "border border-red-400"
|
||||
}`}
|
||||
>
|
||||
<input
|
||||
placeholder="Email"
|
||||
className="w-full text-[16px] leading-6 text-gray-900 px-[10px] py-[14px] border border-gray-300 rounded-lg shadow-sm bg-white"
|
||||
type="text"
|
||||
{...register("email", { required: true })}
|
||||
/>
|
||||
</div>
|
||||
{errors?.email?.message && (
|
||||
<p className="flex items-center justify-between text-[14px] font-normal leading-5 text-red-500">
|
||||
{errors.email.message} <Image src={alert} alt="Alert Icon" />
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-[14px] leading-5 text-gray-700">
|
||||
{t("password")}
|
||||
</label>
|
||||
<div
|
||||
className={`flex items-center border border-gray-300 rounded-lg shadow-sm bg-white${
|
||||
errors?.password?.message && "border border-red-400"
|
||||
}`}
|
||||
>
|
||||
<input
|
||||
placeholder={t("password")}
|
||||
className="w-full text-[16px] leading-6 text-gray-900 px-[10px] py-[14px]"
|
||||
type={showPassword ? "text" : "password"}
|
||||
{...register("password", { required: true })}
|
||||
/>
|
||||
<button
|
||||
onClick={() => setShowPassword((prev) => !prev)}
|
||||
type="button"
|
||||
className="pr-2"
|
||||
>
|
||||
<Image src={showPassword ? eye_on : eye_off} alt="Eye Icon" />
|
||||
</button>
|
||||
</div>
|
||||
{errors?.password?.message && (
|
||||
<p className="flex items-center justify-between text-[14px] font-normal leading-5 text-red-500">
|
||||
{errors.password.message} <Image src={alert} alt="Alert Icon" />
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{errors.root && (
|
||||
<div className="text-red-500">{errors.root.message}</div>
|
||||
)}
|
||||
<button
|
||||
type="submit"
|
||||
className="p-4 h-[50px] w-full font-bold leading-6 text-white bg-light-blue rounded-md"
|
||||
// disabled={isLoading}
|
||||
>
|
||||
{/* {isLoading ? <Loader /> : "Войти"} */}
|
||||
Войти
|
||||
</button>
|
||||
<div className="flex flex-col mt-[36px] gap-4">
|
||||
<button
|
||||
className="p-4 h-[50px] w-full font-bold leading-6 text-white bg-light-blue rounded-md"
|
||||
type="submit"
|
||||
>
|
||||
{loader ? <Loader /> : t("loginBtn")}
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-blue font-semibold pt-2 cursor-pointer">
|
||||
<Link
|
||||
href="/forgot-password-email"
|
||||
className="text-blue font-semibold pt-2 cursor-pointer "
|
||||
>
|
||||
Забыли пароль?
|
||||
{t("passwordForget")}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user