This commit is contained in:
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
/// <reference types="next" />
|
/// <reference types="next" />
|
||||||
/// <reference types="next/image-types/global" />
|
/// <reference types="next/image-types/global" />
|
||||||
import "./.next/dev/types/routes.d.ts";
|
import "./.next/types/routes.d.ts";
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||||
|
|||||||
@@ -1,43 +1,15 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Flex,
|
|
||||||
Heading,
|
|
||||||
Input,
|
|
||||||
Link as ChakraLink,
|
|
||||||
Text,
|
|
||||||
ClientOnly,
|
|
||||||
} from "@chakra-ui/react";
|
|
||||||
import { Button } from "@/components/ui/buttons/button";
|
|
||||||
import { Switch } from "@/components/ui/forms/switch";
|
|
||||||
import { Field } from "@/components/ui/forms/field";
|
|
||||||
import { useTranslations } from "next-intl";
|
|
||||||
import signInImage from "../../../../../public/assets/img/sign-in-image.png";
|
|
||||||
import { InputGroup } from "@/components/ui/forms/input-group";
|
|
||||||
import { BiLock } from "react-icons/bi";
|
|
||||||
import { useForm } from "react-hook-form";
|
|
||||||
import { yupResolver } from "@hookform/resolvers/yup";
|
|
||||||
import * as yup from "yup";
|
|
||||||
import { Link, useRouter } from "@/i18n/navigation";
|
|
||||||
import { MdMail } from "react-icons/md";
|
|
||||||
import { PasswordInput } from "@/components/ui/forms/password-input";
|
|
||||||
import { Skeleton } from "@/components/ui/feedback/skeleton";
|
|
||||||
import { signIn } from "next-auth/react";
|
|
||||||
import { toaster } from "@/components/ui/feedback/toaster";
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
|
import { getTranslations } from "next-intl/server";
|
||||||
|
import SignInForm from "./signin-form";
|
||||||
|
|
||||||
export async function generateMetadata(props: { params: Promise<{ locale: string }> }): Promise<Metadata> {
|
export async function generateMetadata(props: {
|
||||||
|
params: Promise<{ locale: string }>;
|
||||||
|
}): Promise<Metadata> {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
const { locale } = params;
|
const { locale } = params;
|
||||||
const t = await getTranslations({ locale, namespace: "seo" });
|
const t = await getTranslations({ locale, namespace: "seo" });
|
||||||
const siteUrl = process.env.NEXT_PUBLIC_APP_URL || "https://iddaai.com";
|
const siteUrl = process.env.NEXT_PUBLIC_APP_URL || "https://iddaai.com";
|
||||||
|
|
||||||
// Next.js parses route variables automatically, but for canonical we'll just use a clean relative base if available,
|
|
||||||
// or let next.js construct it implicitly from metadataBase if not explicitly specified.
|
|
||||||
// We'll set alternates just for languages based on current path segment as a best effort
|
|
||||||
const pathSegment = "signin";
|
const pathSegment = "signin";
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -53,205 +25,6 @@ export async function generateMetadata(props: { params: Promise<{ locale: string
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const schema = yup.object({
|
export default function SignInPage() {
|
||||||
email: yup.string().email().required(),
|
return <SignInForm />;
|
||||||
password: yup.string().required(),
|
|
||||||
});
|
|
||||||
|
|
||||||
type SignInForm = yup.InferType<typeof schema>;
|
|
||||||
|
|
||||||
const defaultValues = {
|
|
||||||
email: "test@test.com.tr",
|
|
||||||
password: "test1234",
|
|
||||||
};
|
|
||||||
|
|
||||||
function SignInPage() {
|
|
||||||
const t = useTranslations();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
const {
|
|
||||||
handleSubmit,
|
|
||||||
register,
|
|
||||||
formState: { errors },
|
|
||||||
} = useForm<SignInForm>({
|
|
||||||
resolver: yupResolver(schema),
|
|
||||||
mode: "onChange",
|
|
||||||
defaultValues,
|
|
||||||
});
|
|
||||||
|
|
||||||
const onSubmit = async (formData: SignInForm) => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
const res = await signIn("credentials", {
|
|
||||||
redirect: false,
|
|
||||||
email: formData.email,
|
|
||||||
password: formData.password,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res?.error) {
|
|
||||||
throw new Error(res.error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
router.replace("/home");
|
|
||||||
} catch (error) {
|
|
||||||
toaster.error({
|
|
||||||
title: (error as Error).message || "Giriş yaparken hata oluştu!",
|
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box position="relative">
|
|
||||||
<Flex
|
|
||||||
h={{ sm: "initial", md: "75vh", lg: "85vh" }}
|
|
||||||
w="100%"
|
|
||||||
maxW="1044px"
|
|
||||||
mx="auto"
|
|
||||||
justifyContent="space-between"
|
|
||||||
mb="30px"
|
|
||||||
pt={{ sm: "100px", md: "0px" }}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
as="form"
|
|
||||||
onSubmit={handleSubmit(onSubmit)}
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="start"
|
|
||||||
style={{ userSelect: "none" }}
|
|
||||||
w={{ base: "100%", md: "50%", lg: "42%" }}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
w="100%"
|
|
||||||
background="transparent"
|
|
||||||
p="10"
|
|
||||||
mt={{ md: "150px", lg: "80px" }}
|
|
||||||
>
|
|
||||||
<Heading
|
|
||||||
color={{ base: "primary.400", _dark: "primary.200" }}
|
|
||||||
fontSize="32px"
|
|
||||||
mb="10px"
|
|
||||||
fontWeight="bold"
|
|
||||||
>
|
|
||||||
{t("auth.welcome-back")}
|
|
||||||
</Heading>
|
|
||||||
<Text
|
|
||||||
mb="36px"
|
|
||||||
ms="4px"
|
|
||||||
color={{ base: "gray.400", _dark: "white" }}
|
|
||||||
fontWeight="bold"
|
|
||||||
fontSize="14px"
|
|
||||||
>
|
|
||||||
{t("auth.subtitle")}
|
|
||||||
</Text>
|
|
||||||
<Field
|
|
||||||
mb="24px"
|
|
||||||
label={t("email")}
|
|
||||||
errorText={errors.email?.message}
|
|
||||||
invalid={!!errors.email}
|
|
||||||
>
|
|
||||||
<InputGroup w="full" startElement={<MdMail size="1rem" />}>
|
|
||||||
<Input
|
|
||||||
borderRadius="15px"
|
|
||||||
fontSize="sm"
|
|
||||||
type="text"
|
|
||||||
placeholder={t("email")}
|
|
||||||
size="lg"
|
|
||||||
{...register("email")}
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
</Field>
|
|
||||||
<Field
|
|
||||||
mb="24px"
|
|
||||||
label={t("password")}
|
|
||||||
errorText={errors.password?.message}
|
|
||||||
invalid={!!errors.password}
|
|
||||||
>
|
|
||||||
<InputGroup w="full" startElement={<BiLock size="1rem" />}>
|
|
||||||
<PasswordInput
|
|
||||||
borderRadius="15px"
|
|
||||||
fontSize="sm"
|
|
||||||
placeholder={t("password")}
|
|
||||||
size="lg"
|
|
||||||
{...register("password")}
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
</Field>
|
|
||||||
<Field mb="24px">
|
|
||||||
<Switch colorPalette="teal" label={t("auth.remember-me")}>
|
|
||||||
{t("auth.remember-me")}
|
|
||||||
</Switch>
|
|
||||||
</Field>
|
|
||||||
<Field mb="24px">
|
|
||||||
<ClientOnly fallback={<Skeleton height="45px" width="100%" />}>
|
|
||||||
<Button
|
|
||||||
loading={loading}
|
|
||||||
type="submit"
|
|
||||||
bg="primary.400"
|
|
||||||
w="100%"
|
|
||||||
h="45px"
|
|
||||||
color="white"
|
|
||||||
_hover={{
|
|
||||||
bg: "primary.500",
|
|
||||||
}}
|
|
||||||
_active={{
|
|
||||||
bg: "primary.400",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t("auth.sign-in")}
|
|
||||||
</Button>
|
|
||||||
</ClientOnly>
|
|
||||||
</Field>
|
|
||||||
<Flex
|
|
||||||
flexDirection="column"
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems="center"
|
|
||||||
maxW="100%"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
color={{ base: "gray.400", _dark: "white" }}
|
|
||||||
fontWeight="medium"
|
|
||||||
>
|
|
||||||
{t("auth.dont-have-account")}
|
|
||||||
<ChakraLink
|
|
||||||
as={Link}
|
|
||||||
href="/signup"
|
|
||||||
color={{ base: "primary.400", _dark: "primary.200" }}
|
|
||||||
ms="5px"
|
|
||||||
fontWeight="bold"
|
|
||||||
focusRing="none"
|
|
||||||
>
|
|
||||||
{t("auth.sign-up")}
|
|
||||||
</ChakraLink>
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Box
|
|
||||||
display={{ base: "none", md: "block" }}
|
|
||||||
overflowX="hidden"
|
|
||||||
h="100%"
|
|
||||||
w="40vw"
|
|
||||||
position="absolute"
|
|
||||||
right="0px"
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
bgImage={`url(${signInImage.src})`}
|
|
||||||
w="100%"
|
|
||||||
h="100%"
|
|
||||||
bgSize="cover"
|
|
||||||
bgPos="50%"
|
|
||||||
position="absolute"
|
|
||||||
borderBottomLeftRadius="20px"
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SignInPage;
|
|
||||||
|
|||||||
@@ -0,0 +1,229 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Flex,
|
||||||
|
Heading,
|
||||||
|
Input,
|
||||||
|
Link as ChakraLink,
|
||||||
|
Text,
|
||||||
|
ClientOnly,
|
||||||
|
} from "@chakra-ui/react";
|
||||||
|
import { Button } from "@/components/ui/buttons/button";
|
||||||
|
import { Switch } from "@/components/ui/forms/switch";
|
||||||
|
import { Field } from "@/components/ui/forms/field";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import signInImage from "../../../../../public/assets/img/sign-in-image.png";
|
||||||
|
import { InputGroup } from "@/components/ui/forms/input-group";
|
||||||
|
import { BiLock } from "react-icons/bi";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
|
import * as yup from "yup";
|
||||||
|
import { Link, useRouter } from "@/i18n/navigation";
|
||||||
|
import { MdMail } from "react-icons/md";
|
||||||
|
import { PasswordInput } from "@/components/ui/forms/password-input";
|
||||||
|
import { Skeleton } from "@/components/ui/feedback/skeleton";
|
||||||
|
import { signIn } from "next-auth/react";
|
||||||
|
import { toaster } from "@/components/ui/feedback/toaster";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
const schema = yup.object({
|
||||||
|
email: yup.string().email().required(),
|
||||||
|
password: yup.string().required(),
|
||||||
|
});
|
||||||
|
|
||||||
|
type SignInForm = yup.InferType<typeof schema>;
|
||||||
|
|
||||||
|
const defaultValues = {
|
||||||
|
email: "test@test.com.tr",
|
||||||
|
password: "test1234",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function SignInForm() {
|
||||||
|
const t = useTranslations();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const {
|
||||||
|
handleSubmit,
|
||||||
|
register,
|
||||||
|
formState: { errors },
|
||||||
|
} = useForm<SignInForm>({
|
||||||
|
resolver: yupResolver(schema),
|
||||||
|
mode: "onChange",
|
||||||
|
defaultValues,
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = async (formData: SignInForm) => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const res = await signIn("credentials", {
|
||||||
|
redirect: false,
|
||||||
|
email: formData.email,
|
||||||
|
password: formData.password,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res?.error) {
|
||||||
|
throw new Error(res.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
router.replace("/home");
|
||||||
|
} catch (error) {
|
||||||
|
toaster.error({
|
||||||
|
title: (error as Error).message || "Giriş yaparken hata oluştu!",
|
||||||
|
type: "error",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box position="relative">
|
||||||
|
<Flex
|
||||||
|
h={{ sm: "initial", md: "75vh", lg: "85vh" }}
|
||||||
|
w="100%"
|
||||||
|
maxW="1044px"
|
||||||
|
mx="auto"
|
||||||
|
justifyContent="space-between"
|
||||||
|
mb="30px"
|
||||||
|
pt={{ sm: "100px", md: "0px" }}
|
||||||
|
>
|
||||||
|
<Flex
|
||||||
|
as="form"
|
||||||
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="start"
|
||||||
|
style={{ userSelect: "none" }}
|
||||||
|
w={{ base: "100%", md: "50%", lg: "42%" }}
|
||||||
|
>
|
||||||
|
<Flex
|
||||||
|
direction="column"
|
||||||
|
w="100%"
|
||||||
|
background="transparent"
|
||||||
|
p="10"
|
||||||
|
mt={{ md: "150px", lg: "80px" }}
|
||||||
|
>
|
||||||
|
<Heading
|
||||||
|
color={{ base: "primary.400", _dark: "primary.200" }}
|
||||||
|
fontSize="32px"
|
||||||
|
mb="10px"
|
||||||
|
fontWeight="bold"
|
||||||
|
>
|
||||||
|
{t("auth.welcome-back")}
|
||||||
|
</Heading>
|
||||||
|
<Text
|
||||||
|
mb="36px"
|
||||||
|
ms="4px"
|
||||||
|
color={{ base: "gray.400", _dark: "white" }}
|
||||||
|
fontWeight="bold"
|
||||||
|
fontSize="14px"
|
||||||
|
>
|
||||||
|
{t("auth.subtitle")}
|
||||||
|
</Text>
|
||||||
|
<Field
|
||||||
|
mb="24px"
|
||||||
|
label={t("email")}
|
||||||
|
errorText={errors.email?.message}
|
||||||
|
invalid={!!errors.email}
|
||||||
|
>
|
||||||
|
<InputGroup w="full" startElement={<MdMail size="1rem" />}>
|
||||||
|
<Input
|
||||||
|
borderRadius="15px"
|
||||||
|
fontSize="sm"
|
||||||
|
type="text"
|
||||||
|
placeholder={t("email")}
|
||||||
|
size="lg"
|
||||||
|
{...register("email")}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</Field>
|
||||||
|
<Field
|
||||||
|
mb="24px"
|
||||||
|
label={t("password")}
|
||||||
|
errorText={errors.password?.message}
|
||||||
|
invalid={!!errors.password}
|
||||||
|
>
|
||||||
|
<InputGroup w="full" startElement={<BiLock size="1rem" />}>
|
||||||
|
<PasswordInput
|
||||||
|
borderRadius="15px"
|
||||||
|
fontSize="sm"
|
||||||
|
placeholder={t("password")}
|
||||||
|
size="lg"
|
||||||
|
{...register("password")}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</Field>
|
||||||
|
<Field mb="24px">
|
||||||
|
<Switch colorPalette="teal" label={t("auth.remember-me")}>
|
||||||
|
{t("auth.remember-me")}
|
||||||
|
</Switch>
|
||||||
|
</Field>
|
||||||
|
<Field mb="24px">
|
||||||
|
<ClientOnly fallback={<Skeleton height="45px" width="100%" />}>
|
||||||
|
<Button
|
||||||
|
loading={loading}
|
||||||
|
type="submit"
|
||||||
|
bg="primary.400"
|
||||||
|
w="100%"
|
||||||
|
h="45px"
|
||||||
|
color="white"
|
||||||
|
_hover={{
|
||||||
|
bg: "primary.500",
|
||||||
|
}}
|
||||||
|
_active={{
|
||||||
|
bg: "primary.400",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("auth.sign-in")}
|
||||||
|
</Button>
|
||||||
|
</ClientOnly>
|
||||||
|
</Field>
|
||||||
|
<Flex
|
||||||
|
flexDirection="column"
|
||||||
|
justifyContent="center"
|
||||||
|
alignItems="center"
|
||||||
|
maxW="100%"
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
color={{ base: "gray.400", _dark: "white" }}
|
||||||
|
fontWeight="medium"
|
||||||
|
>
|
||||||
|
{t("auth.dont-have-account")}
|
||||||
|
<ChakraLink
|
||||||
|
as={Link}
|
||||||
|
href="/signup"
|
||||||
|
color={{ base: "primary.400", _dark: "primary.200" }}
|
||||||
|
ms="5px"
|
||||||
|
fontWeight="bold"
|
||||||
|
focusRing="none"
|
||||||
|
>
|
||||||
|
{t("auth.sign-up")}
|
||||||
|
</ChakraLink>
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
<Box
|
||||||
|
display={{ base: "none", md: "block" }}
|
||||||
|
overflowX="hidden"
|
||||||
|
h="100%"
|
||||||
|
w="40vw"
|
||||||
|
position="absolute"
|
||||||
|
right="0px"
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
bgImage={`url(${signInImage.src})`}
|
||||||
|
w="100%"
|
||||||
|
h="100%"
|
||||||
|
bgSize="cover"
|
||||||
|
bgPos="50%"
|
||||||
|
position="absolute"
|
||||||
|
borderBottomLeftRadius="20px"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,43 +1,15 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Flex,
|
|
||||||
Input,
|
|
||||||
Link as ChakraLink,
|
|
||||||
Text,
|
|
||||||
ClientOnly,
|
|
||||||
} from "@chakra-ui/react";
|
|
||||||
import signUpImage from "../../../../../public/assets/img/sign-up-image.png";
|
|
||||||
import { Button } from "@/components/ui/buttons/button";
|
|
||||||
import { Field } from "@/components/ui/forms/field";
|
|
||||||
import { useTranslations } from "next-intl";
|
|
||||||
import { useForm } from "react-hook-form";
|
|
||||||
import * as yup from "yup";
|
|
||||||
import { yupResolver } from "@hookform/resolvers/yup";
|
|
||||||
import { InputGroup } from "@/components/ui/forms/input-group";
|
|
||||||
import { BiLock, BiUser } from "react-icons/bi";
|
|
||||||
import { Link } from "@/i18n/navigation";
|
|
||||||
import { MdMail } from "react-icons/md";
|
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
import { PasswordInput } from "@/components/ui/forms/password-input";
|
|
||||||
import { Skeleton } from "@/components/ui/feedback/skeleton";
|
|
||||||
import { authService } from "@/lib/api/auth/service";
|
|
||||||
import { useState } from "react";
|
|
||||||
import { signIn } from "next-auth/react";
|
|
||||||
import { toaster } from "@/components/ui/feedback/toaster";
|
|
||||||
|
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
|
import { getTranslations } from "next-intl/server";
|
||||||
|
import SignUpForm from "./signup-form";
|
||||||
|
|
||||||
export async function generateMetadata(props: { params: Promise<{ locale: string }> }): Promise<Metadata> {
|
export async function generateMetadata(props: {
|
||||||
|
params: Promise<{ locale: string }>;
|
||||||
|
}): Promise<Metadata> {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
const { locale } = params;
|
const { locale } = params;
|
||||||
const t = await getTranslations({ locale, namespace: "seo" });
|
const t = await getTranslations({ locale, namespace: "seo" });
|
||||||
const siteUrl = process.env.NEXT_PUBLIC_APP_URL || "https://iddaai.com";
|
const siteUrl = process.env.NEXT_PUBLIC_APP_URL || "https://iddaai.com";
|
||||||
|
|
||||||
// Next.js parses route variables automatically, but for canonical we'll just use a clean relative base if available,
|
|
||||||
// or let next.js construct it implicitly from metadataBase if not explicitly specified.
|
|
||||||
// We'll set alternates just for languages based on current path segment as a best effort
|
|
||||||
const pathSegment = "signup";
|
const pathSegment = "signup";
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -53,212 +25,6 @@ export async function generateMetadata(props: { params: Promise<{ locale: string
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const schema = yup.object({
|
export default function SignUpPage() {
|
||||||
name: yup.string().required(),
|
return <SignUpForm />;
|
||||||
email: yup.string().email().required(),
|
|
||||||
password: yup.string().min(8).required(),
|
|
||||||
});
|
|
||||||
|
|
||||||
type SignUpForm = yup.InferType<typeof schema>;
|
|
||||||
|
|
||||||
function SignUpPage() {
|
|
||||||
const t = useTranslations();
|
|
||||||
const router = useRouter();
|
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
||||||
|
|
||||||
const {
|
|
||||||
handleSubmit,
|
|
||||||
register,
|
|
||||||
formState: { errors },
|
|
||||||
} = useForm<SignUpForm>({ resolver: yupResolver(schema), mode: "onChange" });
|
|
||||||
|
|
||||||
const onSubmit = async (formData: SignUpForm) => {
|
|
||||||
setIsSubmitting(true);
|
|
||||||
try {
|
|
||||||
await authService.register({
|
|
||||||
email: formData.email,
|
|
||||||
password: formData.password,
|
|
||||||
firstName: formData.name,
|
|
||||||
lastName: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
const res = await signIn("credentials", {
|
|
||||||
redirect: false,
|
|
||||||
email: formData.email,
|
|
||||||
password: formData.password,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res?.error) {
|
|
||||||
throw new Error(res.error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
router.replace("/home");
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof Error && error.message) {
|
|
||||||
toaster.error({
|
|
||||||
title: error.message,
|
|
||||||
type: "error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// other errors are handled by api-service interceptor (toast + 422 display)
|
|
||||||
} finally {
|
|
||||||
setIsSubmitting(false);
|
|
||||||
}
|
|
||||||
return formData;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<Box
|
|
||||||
position="absolute"
|
|
||||||
minH={{ base: "70vh", md: "50vh" }}
|
|
||||||
w={{ md: "calc(100vw - 50px)" }}
|
|
||||||
borderRadius={{ md: "15px" }}
|
|
||||||
left="0"
|
|
||||||
right="0"
|
|
||||||
bgRepeat="no-repeat"
|
|
||||||
overflow="hidden"
|
|
||||||
zIndex="-1"
|
|
||||||
top="0"
|
|
||||||
bgImage={`url(${signUpImage.src})`}
|
|
||||||
bgSize="cover"
|
|
||||||
mx={{ md: "auto" }}
|
|
||||||
mt={{ md: "14px" }}
|
|
||||||
/>
|
|
||||||
<Flex
|
|
||||||
w="full"
|
|
||||||
h="full"
|
|
||||||
direction="column"
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="center"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
fontSize={{ base: "2xl", md: "3xl", lg: "4xl" }}
|
|
||||||
color="white"
|
|
||||||
fontWeight="bold"
|
|
||||||
mt={{ base: "2rem", md: "4.5rem", "2xl": "6.5rem" }}
|
|
||||||
mb={{ base: "2rem", md: "3rem", "2xl": "4rem" }}
|
|
||||||
>
|
|
||||||
{t("auth.create-an-account-now")}
|
|
||||||
</Text>
|
|
||||||
<Flex
|
|
||||||
direction="column"
|
|
||||||
w={{ base: "100%", md: "445px" }}
|
|
||||||
background="transparent"
|
|
||||||
borderRadius="15px"
|
|
||||||
p="10"
|
|
||||||
mx={{ base: "100px" }}
|
|
||||||
bg="bg.panel"
|
|
||||||
boxShadow="0 20px 27px 0 rgb(0 0 0 / 5%)"
|
|
||||||
mb="8"
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
as="form"
|
|
||||||
onSubmit={handleSubmit(onSubmit)}
|
|
||||||
flexDirection="column"
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="start"
|
|
||||||
w="100%"
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
mb="24px"
|
|
||||||
label={t("name")}
|
|
||||||
errorText={errors.name?.message}
|
|
||||||
invalid={!!errors.name}
|
|
||||||
>
|
|
||||||
<InputGroup w="full" startElement={<BiUser size="1rem" />}>
|
|
||||||
<Input
|
|
||||||
borderRadius="15px"
|
|
||||||
fontSize="sm"
|
|
||||||
type="text"
|
|
||||||
placeholder={t("name")}
|
|
||||||
size="lg"
|
|
||||||
{...register("name")}
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
</Field>
|
|
||||||
<Field
|
|
||||||
mb="24px"
|
|
||||||
label={t("email")}
|
|
||||||
errorText={errors.email?.message}
|
|
||||||
invalid={!!errors.email}
|
|
||||||
>
|
|
||||||
<InputGroup w="full" startElement={<MdMail size="1rem" />}>
|
|
||||||
<Input
|
|
||||||
borderRadius="15px"
|
|
||||||
fontSize="sm"
|
|
||||||
type="text"
|
|
||||||
placeholder={t("email")}
|
|
||||||
size="lg"
|
|
||||||
{...register("email")}
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
</Field>
|
|
||||||
<Field
|
|
||||||
mb="24px"
|
|
||||||
label={t("password")}
|
|
||||||
errorText={errors.password?.message}
|
|
||||||
invalid={!!errors.password}
|
|
||||||
>
|
|
||||||
<InputGroup w="full" startElement={<BiLock size="1rem" />}>
|
|
||||||
<PasswordInput
|
|
||||||
borderRadius="15px"
|
|
||||||
fontSize="sm"
|
|
||||||
placeholder={t("password")}
|
|
||||||
size="lg"
|
|
||||||
{...register("password")}
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
</Field>
|
|
||||||
<Field mb="24px">
|
|
||||||
<ClientOnly fallback={<Skeleton height="45px" width="100%" />}>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
bg="primary.400"
|
|
||||||
color="white"
|
|
||||||
fontWeight="bold"
|
|
||||||
w="100%"
|
|
||||||
h="45px"
|
|
||||||
_hover={{
|
|
||||||
bg: "primary.500",
|
|
||||||
}}
|
|
||||||
_active={{
|
|
||||||
bg: "primary.400",
|
|
||||||
}}
|
|
||||||
loading={isSubmitting}
|
|
||||||
>
|
|
||||||
{isSubmitting ? t("auth.registering") : t("auth.sign-up")}
|
|
||||||
</Button>
|
|
||||||
</ClientOnly>
|
|
||||||
</Field>
|
|
||||||
</Flex>
|
|
||||||
<Flex
|
|
||||||
flexDirection="column"
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems="center"
|
|
||||||
maxW="100%"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
color={{ base: "gray.400", _dark: "white" }}
|
|
||||||
fontWeight="medium"
|
|
||||||
>
|
|
||||||
{t("auth.already-have-an-account")}
|
|
||||||
<ChakraLink
|
|
||||||
as={Link}
|
|
||||||
color={{ base: "primary.400", _dark: "primary.200" }}
|
|
||||||
ml="2"
|
|
||||||
href="/signin"
|
|
||||||
fontWeight="bold"
|
|
||||||
focusRing="none"
|
|
||||||
>
|
|
||||||
{t("auth.sign-in")}
|
|
||||||
</ChakraLink>
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SignUpPage;
|
|
||||||
|
|||||||
@@ -0,0 +1,236 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Flex,
|
||||||
|
Input,
|
||||||
|
Link as ChakraLink,
|
||||||
|
Text,
|
||||||
|
ClientOnly,
|
||||||
|
} from "@chakra-ui/react";
|
||||||
|
import signUpImage from "../../../../../public/assets/img/sign-up-image.png";
|
||||||
|
import { Button } from "@/components/ui/buttons/button";
|
||||||
|
import { Field } from "@/components/ui/forms/field";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import * as yup from "yup";
|
||||||
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
|
import { InputGroup } from "@/components/ui/forms/input-group";
|
||||||
|
import { BiLock, BiUser } from "react-icons/bi";
|
||||||
|
import { Link } from "@/i18n/navigation";
|
||||||
|
import { MdMail } from "react-icons/md";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import { PasswordInput } from "@/components/ui/forms/password-input";
|
||||||
|
import { Skeleton } from "@/components/ui/feedback/skeleton";
|
||||||
|
import { authService } from "@/lib/api/auth/service";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { signIn } from "next-auth/react";
|
||||||
|
import { toaster } from "@/components/ui/feedback/toaster";
|
||||||
|
|
||||||
|
const schema = yup.object({
|
||||||
|
name: yup.string().required(),
|
||||||
|
email: yup.string().email().required(),
|
||||||
|
password: yup.string().min(8).required(),
|
||||||
|
});
|
||||||
|
|
||||||
|
type SignUpForm = yup.InferType<typeof schema>;
|
||||||
|
|
||||||
|
export default function SignUpForm() {
|
||||||
|
const t = useTranslations();
|
||||||
|
const router = useRouter();
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
|
const {
|
||||||
|
handleSubmit,
|
||||||
|
register,
|
||||||
|
formState: { errors },
|
||||||
|
} = useForm<SignUpForm>({ resolver: yupResolver(schema), mode: "onChange" });
|
||||||
|
|
||||||
|
const onSubmit = async (formData: SignUpForm) => {
|
||||||
|
setIsSubmitting(true);
|
||||||
|
try {
|
||||||
|
await authService.register({
|
||||||
|
email: formData.email,
|
||||||
|
password: formData.password,
|
||||||
|
firstName: formData.name,
|
||||||
|
lastName: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = await signIn("credentials", {
|
||||||
|
redirect: false,
|
||||||
|
email: formData.email,
|
||||||
|
password: formData.password,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res?.error) {
|
||||||
|
throw new Error(res.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
router.replace("/home");
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error && error.message) {
|
||||||
|
toaster.error({
|
||||||
|
title: error.message,
|
||||||
|
type: "error",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// other errors are handled by api-service interceptor (toast + 422 display)
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
}
|
||||||
|
return formData;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
minH={{ base: "70vh", md: "50vh" }}
|
||||||
|
w={{ md: "calc(100vw - 50px)" }}
|
||||||
|
borderRadius={{ md: "15px" }}
|
||||||
|
left="0"
|
||||||
|
right="0"
|
||||||
|
bgRepeat="no-repeat"
|
||||||
|
overflow="hidden"
|
||||||
|
zIndex="-1"
|
||||||
|
top="0"
|
||||||
|
bgImage={`url(${signUpImage.src})`}
|
||||||
|
bgSize="cover"
|
||||||
|
mx={{ md: "auto" }}
|
||||||
|
mt={{ md: "14px" }}
|
||||||
|
/>
|
||||||
|
<Flex
|
||||||
|
w="full"
|
||||||
|
h="full"
|
||||||
|
direction="column"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="center"
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
fontSize={{ base: "2xl", md: "3xl", lg: "4xl" }}
|
||||||
|
color="white"
|
||||||
|
fontWeight="bold"
|
||||||
|
mt={{ base: "2rem", md: "4.5rem", "2xl": "6.5rem" }}
|
||||||
|
mb={{ base: "2rem", md: "3rem", "2xl": "4rem" }}
|
||||||
|
>
|
||||||
|
{t("auth.create-an-account-now")}
|
||||||
|
</Text>
|
||||||
|
<Flex
|
||||||
|
direction="column"
|
||||||
|
w={{ base: "100%", md: "445px" }}
|
||||||
|
background="transparent"
|
||||||
|
borderRadius="15px"
|
||||||
|
p="10"
|
||||||
|
mx={{ base: "100px" }}
|
||||||
|
bg="bg.panel"
|
||||||
|
boxShadow="0 20px 27px 0 rgb(0 0 0 / 5%)"
|
||||||
|
mb="8"
|
||||||
|
>
|
||||||
|
<Flex
|
||||||
|
as="form"
|
||||||
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
|
flexDirection="column"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="start"
|
||||||
|
w="100%"
|
||||||
|
>
|
||||||
|
<Field
|
||||||
|
mb="24px"
|
||||||
|
label={t("name")}
|
||||||
|
errorText={errors.name?.message}
|
||||||
|
invalid={!!errors.name}
|
||||||
|
>
|
||||||
|
<InputGroup w="full" startElement={<BiUser size="1rem" />}>
|
||||||
|
<Input
|
||||||
|
borderRadius="15px"
|
||||||
|
fontSize="sm"
|
||||||
|
type="text"
|
||||||
|
placeholder={t("name")}
|
||||||
|
size="lg"
|
||||||
|
{...register("name")}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</Field>
|
||||||
|
<Field
|
||||||
|
mb="24px"
|
||||||
|
label={t("email")}
|
||||||
|
errorText={errors.email?.message}
|
||||||
|
invalid={!!errors.email}
|
||||||
|
>
|
||||||
|
<InputGroup w="full" startElement={<MdMail size="1rem" />}>
|
||||||
|
<Input
|
||||||
|
borderRadius="15px"
|
||||||
|
fontSize="sm"
|
||||||
|
type="text"
|
||||||
|
placeholder={t("email")}
|
||||||
|
size="lg"
|
||||||
|
{...register("email")}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</Field>
|
||||||
|
<Field
|
||||||
|
mb="24px"
|
||||||
|
label={t("password")}
|
||||||
|
errorText={errors.password?.message}
|
||||||
|
invalid={!!errors.password}
|
||||||
|
>
|
||||||
|
<InputGroup w="full" startElement={<BiLock size="1rem" />}>
|
||||||
|
<PasswordInput
|
||||||
|
borderRadius="15px"
|
||||||
|
fontSize="sm"
|
||||||
|
placeholder={t("password")}
|
||||||
|
size="lg"
|
||||||
|
{...register("password")}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</Field>
|
||||||
|
<Field mb="24px">
|
||||||
|
<ClientOnly fallback={<Skeleton height="45px" width="100%" />}>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
bg="primary.400"
|
||||||
|
color="white"
|
||||||
|
fontWeight="bold"
|
||||||
|
w="100%"
|
||||||
|
h="45px"
|
||||||
|
_hover={{
|
||||||
|
bg: "primary.500",
|
||||||
|
}}
|
||||||
|
_active={{
|
||||||
|
bg: "primary.400",
|
||||||
|
}}
|
||||||
|
loading={isSubmitting}
|
||||||
|
>
|
||||||
|
{isSubmitting ? t("auth.registering") : t("auth.sign-up")}
|
||||||
|
</Button>
|
||||||
|
</ClientOnly>
|
||||||
|
</Field>
|
||||||
|
</Flex>
|
||||||
|
<Flex
|
||||||
|
flexDirection="column"
|
||||||
|
justifyContent="center"
|
||||||
|
alignItems="center"
|
||||||
|
maxW="100%"
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
color={{ base: "gray.400", _dark: "white" }}
|
||||||
|
fontWeight="medium"
|
||||||
|
>
|
||||||
|
{t("auth.already-have-an-account")}
|
||||||
|
<ChakraLink
|
||||||
|
as={Link}
|
||||||
|
color={{ base: "primary.400", _dark: "primary.200" }}
|
||||||
|
ml="2"
|
||||||
|
href="/signin"
|
||||||
|
fontWeight="bold"
|
||||||
|
focusRing="none"
|
||||||
|
>
|
||||||
|
{t("auth.sign-in")}
|
||||||
|
</ChakraLink>
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { getTranslations } from "next-intl/server";
|
||||||
|
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user