main
Deploy Iddaai Frontend / build-and-deploy (push) Successful in 3m34s

This commit is contained in:
2026-05-04 19:21:48 +03:00
parent 0d194f7409
commit f72857a3b2
6 changed files with 483 additions and 478 deletions
+8 -235
View File
@@ -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 { 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 { locale } = params;
const t = await getTranslations({ locale, namespace: "seo" });
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";
return {
@@ -53,205 +25,6 @@ export async function generateMetadata(props: { params: Promise<{ locale: string
};
}
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",
};
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 function SignInPage() {
return <SignInForm />;
}
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>
);
}
+8 -242
View File
@@ -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 { 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 { locale } = params;
const t = await getTranslations({ locale, namespace: "seo" });
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";
return {
@@ -53,212 +25,6 @@ export async function generateMetadata(props: { params: Promise<{ locale: string
};
}
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>;
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 function SignUpPage() {
return <SignUpForm />;
}
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
View File
@@ -1,4 +1,5 @@
import React from 'react';
import { getTranslations } from "next-intl/server";
import { Metadata } from "next";