diff --git a/api.ts b/api.ts index dcb654ff4..cbf83a241 100644 --- a/api.ts +++ b/api.ts @@ -1,5 +1,4 @@ import { CODEIT_BASE_URL } from "@/constants"; -import { FormValues } from "./common/Auth/Form"; import { FolderData, LinkData, @@ -9,6 +8,7 @@ import { Response, UserData, } from "./types/api"; +import { FormValues } from "./types/form"; interface TokenProp { token: string; @@ -134,70 +134,72 @@ export async function getLinksByFolderId({ // POST -export async function postEmailCheck(email: string): Promise { - const response = await fetch(`${CODEIT_BASE_URL}/check-email`, { +export async function postEmailCheck(email: string): Promise { + const response = await fetch(`${CODEIT_BASE_URL}/users/check-email`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ email }), }); + const result = await response.json(); if (response.status === 409) { - const data = await response.json(); - return data.error.message; + return result.message; } if (!response.ok) { throw new Error("잘못된 요청입니다."); } - return; + return result; } interface postData { - data: { - accessToken: string; - refreshToken: string; - }; + data: + | { + accessToken: string; + refreshToken: string; + } + | { message: string }; } export async function postSignup({ email, password, -}: FormValues): Promise { - const response = await fetch(`${CODEIT_BASE_URL}/sign-up`, { +}: FormValues): Promise { + const response = await fetch(`${CODEIT_BASE_URL}/auth/sign-up`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ email, password }), }); - const data = await response.json(); if (!response.ok) { - return data.error.message; + throw new Error("잘못된 요청입니다."); } - - return data; } export async function postSignin({ email, password, }: FormValues): Promise { - const response = await fetch(`${CODEIT_BASE_URL}/sign-in`, { + const response = await fetch(`${CODEIT_BASE_URL}/auth/sign-in`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ email, password }), }); - const data = await response.json(); if (!response.ok) { - return data.error.message; + throw new Error("잘못된 요청입니다."); } - return data; + const result = await response.json(); + + return result; } + +// 탠스택 쿼리와 미들웨어 api 라우트 구현 diff --git a/common/Auth/Form/index.module.css b/common/Auth/Form/index.module.css deleted file mode 100644 index 3c25a5f0b..000000000 --- a/common/Auth/Form/index.module.css +++ /dev/null @@ -1,18 +0,0 @@ -.formContainer { - width: 100%; - max-width: 400px; - display: flex; - flex-direction: column; - gap: 15px; -} - -.submitBtn { - font-size: 20px; - width: 100%; - height: 60px; - border: none; - border-radius: 8px; - color: var(--white); - background: var(--primary-gradient); - margin-bottom: 15px; -} diff --git a/common/Auth/Form/index.tsx b/common/Auth/Form/index.tsx deleted file mode 100644 index 30018ddd6..000000000 --- a/common/Auth/Form/index.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { SubmitHandler, UseFormHandleSubmit } from "react-hook-form"; -import styles from "./index.module.css"; -import { ReactNode } from "react"; -import { TProps } from "../Header"; - -export interface FormValues { - email: string; - password: string; - passwordConfirm?: string; -} - -export interface AuthFormProps { - children?: ReactNode; - onSubmit: SubmitHandler; - handleSubmit: UseFormHandleSubmit; - purpose: TProps; -} - -function AuthForm({ - children, - onSubmit, - handleSubmit, - purpose, -}: AuthFormProps) { - return ( -
- {children} - -
- ); -} - -export default AuthForm; diff --git a/common/Auth/Input/index.tsx b/common/Auth/Input/index.tsx deleted file mode 100644 index bc60a6ca8..000000000 --- a/common/Auth/Input/index.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import React, { HTMLInputTypeAttribute, forwardRef, useState } from "react"; -import { FaEyeSlash, FaEye } from "react-icons/fa"; -import styles from "./index.module.css"; -import { - FieldError, - FieldErrorsImpl, - FieldValues, - Merge, -} from "react-hook-form"; -import classNames from "classnames"; - -interface AuthInputProps { - label: string; - type: HTMLInputTypeAttribute; - placeholder: string; - register: FieldValues; - error?: FieldError | Merge; -} - -function AuthInput( - { label, type, placeholder, register, error }: AuthInputProps, - ref: React.ForwardedRef -) { - const [isVisible, setIsVisible] = useState(false); - - const handleToggleVisibility = () => { - if (typeof ref !== "function" && ref?.current) { - ref.current.type = isVisible ? "password" : "text"; - } - setIsVisible(!isVisible); - }; - - return ( -
- - { - register.ref(e); - if (typeof ref === "function") { - ref(e); - } else if (ref) { - ref.current = e; - } - }} - /> - {error && ( -

{error.message?.toString()}

- )} - {type === "password" && ( - - )} -
- ); -} - -export default forwardRef(AuthInput); diff --git a/common/Auth/Input/index.module.css b/components/Auth/Form.module.css similarity index 59% rename from common/Auth/Input/index.module.css rename to components/Auth/Form.module.css index 7267d1b76..e411cf4da 100644 --- a/common/Auth/Input/index.module.css +++ b/components/Auth/Form.module.css @@ -1,4 +1,23 @@ -.container { +.formContainer { + width: 100%; + max-width: 400px; + display: flex; + flex-direction: column; + gap: 15px; +} + +.submitButton { + font-size: 20px; + width: 100%; + height: 60px; + border: none; + border-radius: 8px; + color: var(--white); + background: var(--primary-gradient); + margin-bottom: 15px; +} + +.inputContainer { width: 100%; position: relative; } @@ -10,6 +29,7 @@ border: 1px solid var(--gray-500); border-radius: 8px; margin-bottom: 10px; + font-size: 16px; } .inputWrapper:focus { @@ -18,15 +38,15 @@ .inputLabel { display: inline-block; - margin-bottom: 15px; + margin-bottom: 10px; } -.eyeSlash { +.eyeIcon { border: none; background: none; position: absolute; right: 16px; - top: 53px; + top: 48px; } .errorBorder { diff --git a/common/Auth/Header/index.module.css b/components/Auth/Header/index.module.css similarity index 100% rename from common/Auth/Header/index.module.css rename to components/Auth/Header/index.module.css diff --git a/common/Auth/Header/index.tsx b/components/Auth/Header/index.tsx similarity index 100% rename from common/Auth/Header/index.tsx rename to components/Auth/Header/index.tsx diff --git a/components/Auth/SigninForm/index.tsx b/components/Auth/SigninForm/index.tsx new file mode 100644 index 000000000..29fc7c221 --- /dev/null +++ b/components/Auth/SigninForm/index.tsx @@ -0,0 +1,78 @@ +import { useForm } from "react-hook-form"; +import useSignin from "@/hooks/auth/useSignin"; +import { useStoreState } from "@/hooks/state"; +import classNames from "classnames"; +import { FormValues } from "@/types/form"; +import { EMAIL_PATTERN, PW_PATTERN } from "@/constants"; +import { FaEye, FaEyeSlash } from "react-icons/fa"; +import styles from "../Form.module.css"; + +function SigninForm() { + const { + register, + handleSubmit, + formState: { errors }, + } = useForm({ mode: "onBlur" }); + const { currentType, toggleType } = useStoreState(); + const { handleSignin } = useSignin(); + + return ( +
+
+ + + {errors.email && ( +

{errors.email.message}

+ )} +
+
+ + + {errors.password && ( +

{errors.password.message}

+ )} + +
+ +
+ ); +} + +export default SigninForm; diff --git a/components/Auth/SignupForm/index.tsx b/components/Auth/SignupForm/index.tsx new file mode 100644 index 000000000..c92d6447a --- /dev/null +++ b/components/Auth/SignupForm/index.tsx @@ -0,0 +1,107 @@ +import classNames from "classnames"; +import useSignup from "@/hooks/auth/useSignup"; +import { useStoreState } from "@/hooks/state"; +import { EMAIL_PATTERN, PW_PATTERN } from "@/constants"; +import { FaEye, FaEyeSlash } from "react-icons/fa"; +import styles from "../Form.module.css"; + +function SignupForm() { + const { + handleEmailCheck, + handleSignup, + register, + handleSubmit, + formState: { errors }, + watch, + emailCheckMutation, + } = useSignup(); + const password = watch("password"); + const { currentType, toggleType } = useStoreState(); + + return ( +
+
+ + handleEmailCheck(e), + })} + /> + {errors.email && ( +

{errors.email.message}

+ )} +
+
+ + + {errors.password && ( +

{errors.password.message}

+ )} + +
+
+ + + value === password || "비밀번호와 일치하지 않습니다", + })} + /> + {errors.passwordConfirm && ( +

+ {errors.passwordConfirm.message} +

+ )} + +
+ +
+ ); +} + +export default SignupForm; diff --git a/constants/index.ts b/constants/index.ts index 412792837..76e2e5a76 100644 --- a/constants/index.ts +++ b/constants/index.ts @@ -1,7 +1,8 @@ //API export const KAKAO_KEY = "a841341da0099a5d1291638b48030745"; -export const CODEIT_BASE_URL = "https://bootcamp-api.codeit.kr/api"; +export const CODEIT_BASE_URL = + "https://bootcamp-api.codeit.kr/api/linkbrary/v1"; export const SAMPLE_USER_ID = 1; export const SOCIALLINKS = [ { href: "https://www.kakaocorp.com/page", src: "/images/kakaotalk.png" }, @@ -21,12 +22,3 @@ export const YEAR: number = 12 * MONTH; export const PW_PATTERN = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/; export const EMAIL_PATTERN = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/; - -//Token - -export const LOCAL_ACCESSTOKEN: string | false | null = - typeof window !== "undefined" && localStorage.getItem("accessToken"); - -//Hook - -export const IS_CLIENT = typeof window !== "undefined"; diff --git a/hooks/api/useFolder.ts b/hooks/api/useFolder.ts index 8a8a07da5..b90e2dad5 100644 --- a/hooks/api/useFolder.ts +++ b/hooks/api/useFolder.ts @@ -1,5 +1,4 @@ import { getFolders } from "@/api"; -import { IS_CLIENT } from "@/constants"; import { useQuery } from "@tanstack/react-query"; import { QueryTokenProp } from "./useUser"; import { useRouter } from "next/router"; @@ -23,7 +22,7 @@ export function useFolder({ localAccessToken }: QueryTokenProp) { }); return data; }, - enabled: IS_CLIENT, + enabled: !!window, }); return { folders, isLoadingFolders, isErrorFolders }; diff --git a/hooks/api/useLink.ts b/hooks/api/useLink.ts index 049f6143b..40c8a3abe 100644 --- a/hooks/api/useLink.ts +++ b/hooks/api/useLink.ts @@ -1,5 +1,4 @@ import { getLinksByFolderId } from "@/api"; -import { IS_CLIENT } from "@/constants"; import { useQuery } from "@tanstack/react-query"; import { QueryTokenProp } from "./useUser"; import { useRouter } from "next/router"; @@ -28,7 +27,7 @@ export function useLink({ folderId, localAccessToken }: LinkProps) { }); return data; }, - enabled: IS_CLIENT, + enabled: !!window, }); return { links, isLoadingLinks, isErrorLinks }; diff --git a/hooks/api/useUser.ts b/hooks/api/useUser.ts index 28cb4d1e0..b9ae34d90 100644 --- a/hooks/api/useUser.ts +++ b/hooks/api/useUser.ts @@ -1,5 +1,4 @@ import { getUserByToken } from "@/api"; -import { IS_CLIENT } from "@/constants"; import { useQuery } from "@tanstack/react-query"; import { useRouter } from "next/router"; @@ -25,7 +24,7 @@ export function useUser({ localAccessToken }: QueryTokenProp) { return data; }, - enabled: IS_CLIENT, + enabled: !!window, }); return { user, isLoadingUser, isErrorUser }; diff --git a/hooks/auth/useSignin.ts b/hooks/auth/useSignin.ts index 44050aa93..e71798226 100644 --- a/hooks/auth/useSignin.ts +++ b/hooks/auth/useSignin.ts @@ -1,47 +1,19 @@ import { postSignin } from "@/api"; -import { FormValues } from "@/common/Auth/Form"; -import { LOCAL_ACCESSTOKEN } from "@/constants"; +import { FormValues } from "@/types/form"; +import { useMutation } from "@tanstack/react-query"; import { useRouter } from "next/router"; -import { SubmitHandler, UseFormSetError } from "react-hook-form"; +import { SubmitHandler } from "react-hook-form"; -interface UseSigninProp { - setError: UseFormSetError; -} - -export function useSignin({ setError }: UseSigninProp) { +function useSignin() { const router = useRouter(); + const mutation = useMutation({ mutationFn: postSignin }); - const handleSignin: SubmitHandler = async (data) => { - const { email, password } = data; - - try { - const result = await postSignin({ email, password }); - const resultAccessToken = result.data.accessToken; - - if (LOCAL_ACCESSTOKEN === resultAccessToken) { - router.replace("/folder"); - return; - } - - if (typeof result === "string") { - const fields: (keyof FormValues)[] = ["email", "password"]; - - fields.forEach((field) => { - setError(field, { - type: "manual", - message: "이메일 또는 비밀번호를 확인해 주세요", - }); - }); - - return; - } - - localStorage.setItem("accessToken", resultAccessToken); - router.replace("/folder"); - } catch (error) { - console.error(error); - } + const handleSignin: SubmitHandler = ({ email, password }) => { + mutation.mutate({ email, password }); + router.replace("/"); }; return { handleSignin }; } + +export default useSignin; diff --git a/hooks/auth/useSignup.ts b/hooks/auth/useSignup.ts new file mode 100644 index 000000000..3ab04a6fc --- /dev/null +++ b/hooks/auth/useSignup.ts @@ -0,0 +1,58 @@ +import { postEmailCheck, postSignup } from "@/api"; +import { FormValues } from "@/types/form"; +import { useMutation } from "@tanstack/react-query"; +import { useRouter } from "next/router"; +import { SubmitHandler, useForm } from "react-hook-form"; + +function useSignup() { + const router = useRouter(); + const { + register, + handleSubmit, + formState: { errors }, + watch, + setError, + } = useForm({ mode: "onBlur" }); + const emailCheckMutation = useMutation({ + mutationFn: postEmailCheck, + onSuccess: (data) => { + if (typeof data === "string") { + setError( + "email", + { + type: "validate", + message: data, + }, + { shouldFocus: true } + ); + } + }, + }); + const signupMutation = useMutation({ mutationFn: postSignup }); + + const handleEmailCheck = (e: React.FocusEvent) => { + const email = e.target.value; + + if (email.length === 0) return; + + emailCheckMutation.mutate(email); + }; + + const handleSignup: SubmitHandler = ({ email, password }) => { + signupMutation.mutate({ email, password }); + + router.replace("/signin"); + }; + + return { + handleEmailCheck, + handleSignup, + register, + handleSubmit, + formState: { errors }, + watch, + emailCheckMutation, + }; +} + +export default useSignup; diff --git a/hooks/state/index.ts b/hooks/state/index.ts index 534327947..b0bdad7d5 100644 --- a/hooks/state/index.ts +++ b/hooks/state/index.ts @@ -4,25 +4,42 @@ import { create } from "zustand"; interface StoreStateProps { user: UserData | null; setUser: (user: UserData) => void; + folders: NewFolderData[] | FolderData[] | null; setFolders: (folders: NewFolderData[] | FolderData[]) => void; + links?: LinkData[]; setLinks: (links?: LinkData[]) => void; + isLoadingWindow: boolean; setIsLoadingWindow: (isLoadingWindow: boolean) => void; + searchQuery?: string; setSearchQuery: (searchQuery?: string) => void; + + currentType: string; + toggleType: () => void; } export const useStoreState = create((set) => ({ user: null, setUser: (user: UserData) => set({ user }), + folders: null, setFolders: (folders: NewFolderData[] | FolderData[]) => set({ folders }), + links: [], setLinks: (links?: LinkData[]) => set({ links }), + isLoadingWindow: true, setIsLoadingWindow: (isLoadingWindow: boolean) => set({ isLoadingWindow }), + searchQuery: "", setSearchQuery: (searchQuery?: string) => set({ searchQuery }), + + currentType: "password", + toggleType: () => + set((state) => ({ + currentType: state.currentType === "password" ? "text" : "password", + })), })); diff --git a/pages/folder/[folderId].page.tsx b/pages/folder/[folderId].page.tsx index ad5e63d90..e03d409dc 100644 --- a/pages/folder/[folderId].page.tsx +++ b/pages/folder/[folderId].page.tsx @@ -2,7 +2,6 @@ import { useEffect } from "react"; import { useRouter } from "next/router"; import { useStoreState } from "@/hooks/state"; import { useLink } from "@/hooks/api/useLink"; -import { LOCAL_ACCESSTOKEN } from "@/constants"; import LinkListPageLayout from "@/components/LinkListPageLayout"; import LinkCards from "@/components/LinkCards"; import LinkAddForm from "@/components/LinkAddForm"; @@ -13,12 +12,14 @@ import styles from "@/styles/LinkListPage.module.css"; function Folder() { const { user, folders, setLinks } = useStoreState(); + const localAccessToken = + typeof window !== "undefined" && localStorage.getItem("accessToken"); const router = useRouter(); const { folderId } = router.query; const id = Number(folderId); const { links } = useLink({ folderId: id, - localAccessToken: LOCAL_ACCESSTOKEN, + localAccessToken, }); const handleClickToSharedPage = () => { @@ -28,9 +29,11 @@ function Folder() { useEffect(() => { if (!user) { router.replace("/signin"); + return; } if (!folders) { router.replace("/folder"); + return; } if (links) { setLinks(links); diff --git a/pages/folder/index.page.tsx b/pages/folder/index.page.tsx index 747bcbaf0..ce1224309 100644 --- a/pages/folder/index.page.tsx +++ b/pages/folder/index.page.tsx @@ -3,7 +3,6 @@ import { useStoreState } from "@/hooks/state"; import { useUser } from "@/hooks/api/useUser"; import { useFolder } from "@/hooks/api/useFolder"; import { useLink } from "@/hooks/api/useLink"; -import { IS_CLIENT, LOCAL_ACCESSTOKEN } from "@/constants"; import LinkListPageLayout from "@/components/LinkListPageLayout"; import LinkCards from "@/components/LinkCards"; import LinkAddForm from "@/components/LinkAddForm"; @@ -13,20 +12,22 @@ import EmptyLinkCards from "@/components/EmptyLinkCards"; import styles from "@/styles/LinkListPage.module.css"; function FolderPage() { + const localAccessToken = + typeof window !== "undefined" && localStorage.getItem("accessToken"); const { setUser, setFolders, setIsLoadingWindow, isLoadingWindow } = useStoreState(); const { user, isLoadingUser, isErrorUser } = useUser({ - localAccessToken: LOCAL_ACCESSTOKEN, + localAccessToken, }); const { folders, isLoadingFolders, isErrorFolders } = useFolder({ - localAccessToken: LOCAL_ACCESSTOKEN, + localAccessToken, }); const { links } = useLink({ - localAccessToken: LOCAL_ACCESSTOKEN, + localAccessToken, }); useEffect(() => { - if (IS_CLIENT) { + if (typeof window !== "undefined") { setIsLoadingWindow(false); if (user) { diff --git a/pages/index.page.tsx b/pages/index.page.tsx index cebe12146..d59bff9af 100644 --- a/pages/index.page.tsx +++ b/pages/index.page.tsx @@ -1,34 +1,18 @@ -import { LOCAL_ACCESSTOKEN } from "@/constants"; import Link from "next/link"; -import { useRouter } from "next/router"; +import styles from "@/styles/Home.module.css"; export default function Home() { - const router = useRouter(); - - const handleAutoSignin = () => { - if (LOCAL_ACCESSTOKEN) { - return router.push("/folder"); - } - - return router.push("/signin"); - }; - return ( -
- - 회원가입 - 폴더페이지 -
+
+

링크브러리

+
+ + 로그인 + + + 회원가입 + +
+
); } diff --git a/pages/shared/[folderId].page.tsx b/pages/shared/[folderId].page.tsx index b7bd39bc5..c8bd6d985 100644 --- a/pages/shared/[folderId].page.tsx +++ b/pages/shared/[folderId].page.tsx @@ -1,6 +1,5 @@ import { useRouter } from "next/router"; import { useLink } from "@/hooks/api/useLink"; -import { LOCAL_ACCESSTOKEN } from "@/constants"; import LinkListPageLayout from "@/components/LinkListPageLayout"; import UserProfileAndTitle from "@/components/UserProfileAndTitle"; import SearchBar from "@/common/SearchBar"; @@ -10,11 +9,13 @@ import styles from "@/styles/LinkListPage.module.css"; function SharedFolder() { const { query } = useRouter(); + const localAccessToken = + typeof window !== "undefined" && localStorage.getItem("accessToken"); const { folderId } = query; const id = Number(folderId); const { links } = useLink({ folderId: id, - localAccessToken: LOCAL_ACCESSTOKEN, + localAccessToken, }); return ( diff --git a/pages/signin/index.page.tsx b/pages/signin/index.page.tsx index b1d23f5f2..b42d8cf50 100644 --- a/pages/signin/index.page.tsx +++ b/pages/signin/index.page.tsx @@ -1,56 +1,14 @@ -import AuthHeader from "@/common/Auth/Header"; import SocialAuthBox from "@/components/SocialAuthBox"; import styles from "@/styles/Auth.module.css"; -import AuthForm, { FormValues } from "@/common/Auth/Form"; -import AuthInput from "@/common/Auth/Input"; -import { useForm } from "react-hook-form"; -import { EMAIL_PATTERN } from "@/constants"; -import { useRef } from "react"; -import { useSignin } from "@/hooks/auth/useSignin"; +import AuthHeader from "@/components/Auth/Header"; +import SigninForm from "@/components/Auth/SigninForm"; function Signin() { - const passwordRef = useRef(null); - const { - register, - formState: { errors }, - handleSubmit, - setError, - } = useForm({ mode: "onBlur" }); - const { handleSignin } = useSignin({ setError }); - return (
- - - - +
diff --git a/pages/signup/index.page.tsx b/pages/signup/index.page.tsx index 95a3c4d50..3a994481e 100644 --- a/pages/signup/index.page.tsx +++ b/pages/signup/index.page.tsx @@ -1,108 +1,15 @@ -import { SubmitHandler, useForm } from "react-hook-form"; -import { postEmailCheck, postSignup } from "@/api"; -import { EMAIL_PATTERN, PW_PATTERN } from "@/constants"; -import AuthHeader from "@/common/Auth/Header"; -import AuthForm, { FormValues } from "@/common/Auth/Form"; -import AuthInput from "@/common/Auth/Input"; +import React from "react"; import SocialAuthBox from "@/components/SocialAuthBox"; +import AuthHeader from "@/components/Auth/Header"; +import SignupForm from "@/components/Auth/SignupForm"; import styles from "@/styles/Auth.module.css"; -import React from "react"; -import { useRouter } from "next/router"; function Signup() { - const router = useRouter(); - const { - register, - handleSubmit, - formState: { errors }, - watch, - setError, - } = useForm({ mode: "onBlur" }); - - const password = watch("password"); - - const handleEmailCheck = async (e: React.FocusEvent) => { - const email = e.target.value; - try { - const result = await postEmailCheck(email); - console.log("result", result); - - if (result) { - setError( - "email", - { type: "onBlur", message: result }, - { shouldFocus: true } - ); - } - - return; - } catch (error) { - console.error(error); - } - }; - - const handleSignup: SubmitHandler = async ({ - email, - password, - }) => { - try { - const result = await postSignup({ email, password }); - - localStorage.setItem("accessToken", result.data.accessToken); - router.push("/folder"); - } catch (error) { - console.error(error); - } - }; - return (
- - handleEmailCheck(e), - })} - error={errors.email} - /> - - - value === password || "비밀번호와 일치하지 않습니다", - })} - error={errors.passwordConfirm} - /> - +
diff --git a/styles/Home.module.css b/styles/Home.module.css new file mode 100644 index 000000000..728eda4c0 --- /dev/null +++ b/styles/Home.module.css @@ -0,0 +1,34 @@ +.container { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: 100vh; + gap: 20px; +} + +.title { + color: var(--primary); + font-size: 156px; + font-weight: 900; +} + +.linkWrapper { + display: flex; + gap: 15px; + margin-top: 20px; +} + +.linkButton { + padding: 10px 40px; + font-size: larger; + text-decoration: none; + background-color: var(--primary); + color: white; + border-radius: 8px; + transition: background-color 0.1s ease-in; +} + +.linkButton:hover { + background-color: var(--gray-600); +} diff --git a/types/form/index.ts b/types/form/index.ts new file mode 100644 index 000000000..0297c7f59 --- /dev/null +++ b/types/form/index.ts @@ -0,0 +1,16 @@ +import { TProps } from "@/components/Auth/Header"; +import { ReactNode } from "react"; +import { SubmitHandler, UseFormHandleSubmit } from "react-hook-form"; + +export interface FormValues { + email: string; + password: string; + passwordConfirm?: string; +} + +export interface AuthFormProps { + children?: ReactNode; + onSubmit: SubmitHandler; + handleSubmit: UseFormHandleSubmit; + purpose: TProps; +}