diff --git a/.gitignore b/.gitignore index fd3dbb5..8585166 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts +.env diff --git a/src/app/reset-password/[token]/_actions/actions.ts b/src/app/reset-password/[token]/_actions/actions.ts new file mode 100644 index 0000000..038e18d --- /dev/null +++ b/src/app/reset-password/[token]/_actions/actions.ts @@ -0,0 +1,21 @@ +'use server'; + +import { ActionStatus } from '@/enums/ActionStatus'; + +export const requestTokenValidation = async (token: string) => { + try { + // api 개발 완료되면 엔드 포인트 추가 예정 + const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ token }), + }); + + return { status: ActionStatus.Success, fields: response.json() }; + } catch (error) { + alert('토큰 확인 요망 👾'); + return { status: ActionStatus.Error, issues: [error] }; + } +}; diff --git a/src/app/reset-password/[token]/page.tsx b/src/app/reset-password/[token]/page.tsx index 1fbe882..c31ee34 100644 --- a/src/app/reset-password/[token]/page.tsx +++ b/src/app/reset-password/[token]/page.tsx @@ -3,8 +3,12 @@ import Link from 'next/link'; import ResetPasswordForm from '../_forms/ResetPasswordForm'; import { HeadingWithDescription } from '@/components/HeadingWithDescription/HeadingWithDescription'; import { redirect } from 'next/navigation'; +import { requestTokenValidation } from './_actions/actions'; + +export default async function Page({ params: { token } }: { params: { token: string } }) { + const response = await requestTokenValidation(token); + const { email } = await response.fields; -export default function Page() { const handleSuccess = async () => { 'use server'; @@ -21,7 +25,7 @@ export default function Page() { - + ); } diff --git a/src/app/reset-password/_forms/ResetPasswordForm.action.ts b/src/app/reset-password/_forms/ResetPasswordForm.action.ts index da182d6..3d9b288 100644 --- a/src/app/reset-password/_forms/ResetPasswordForm.action.ts +++ b/src/app/reset-password/_forms/ResetPasswordForm.action.ts @@ -11,6 +11,14 @@ export async function resetPassword(prevState: FormState, data: FormData): Promi setTimeout(() => resolve(null), 1500); }); + // const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/password/confirm`, { + // method: 'POST', + // headers: { + // 'Content-Type': 'application/json', + // }, + // body: JSON.stringify(Object.fromEntries(data)), + // }); + // return { status: ActionStatus.Error, issues: ['에러여 에러'] }; return { status: ActionStatus.Success, fields: Object.fromEntries(data) as Record }; } diff --git a/src/app/reset-password/_forms/ResetPasswordForm.tsx b/src/app/reset-password/_forms/ResetPasswordForm.tsx index 77e77c6..c4e9423 100644 --- a/src/app/reset-password/_forms/ResetPasswordForm.tsx +++ b/src/app/reset-password/_forms/ResetPasswordForm.tsx @@ -11,10 +11,26 @@ import { Control, useForm } from 'react-hook-form'; import { z } from 'zod'; import { resetPassword } from './ResetPasswordForm.action'; -const formSchema = z.object({ - password: z.string(), - passwordConfirm: z.string(), -}); +const passwordRegex = new RegExp(/^(?=.*[a-zA-Z])(?=.*[!@#$%^*+=-])(?=.*[0-9]).{8,15}$/); + +const formSchema = z + .object({ + password: z + .string() + .min(8, { message: '비밀번호는 최소 8자 이상이어야 합니다.' }) + .max(15, { message: '비밀번호는 최대 15자 이내이어야 합니다.' }) + .regex(passwordRegex, { message: '영문, 숫자, 특수문자 포함해서 머시기해라.' }), + passwordConfirm: z.string(), + }) + .superRefine(({ password, passwordConfirm }, ctx) => { + if (password !== passwordConfirm) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: '비밀번호가 일치하지 않네요.', + path: ['passwordConfirm'], + }); + } + }); export type ResetPasswordRequest = z.infer; @@ -25,9 +41,10 @@ const initialValues: ResetPasswordRequest = { type ResetPasswordFormProps = { onSuccess: () => void; + email: string; }; -export default function ResetPasswordForm({ onSuccess }: ResetPasswordFormProps) { +export default function ResetPasswordForm({ onSuccess, email }: ResetPasswordFormProps) { const [isPending, startTransition] = useTransition(); const [state, setState] = useState({ @@ -52,6 +69,9 @@ export default function ResetPasswordForm({ onSuccess }: ResetPasswordFormProps) const formData = new FormData(formRef.current); + // input hidden 대신 email 값을 formData에 포함시켰어요. + formData.append('email', email); + setState({ status: ActionStatus.Idle, fields: { ...(Object.fromEntries(formData) as Record) },