diff --git a/apps/community/src/app/my/change-password/page.tsx b/apps/community/src/app/my/change-password/page.tsx new file mode 100644 index 0000000..ecf92f6 --- /dev/null +++ b/apps/community/src/app/my/change-password/page.tsx @@ -0,0 +1,16 @@ +import ChangePasswordForm from '~/components/my/change-password/change-password-form'; +import { PageHeader } from '~/components/page-header'; + +const ChangePasswordPage = () => { + return ( + <> + + + + ); +}; + +export default ChangePasswordPage; diff --git a/apps/community/src/components/my/change-password/change-password-form.css.ts b/apps/community/src/components/my/change-password/change-password-form.css.ts new file mode 100644 index 0000000..bb35c80 --- /dev/null +++ b/apps/community/src/components/my/change-password/change-password-form.css.ts @@ -0,0 +1,27 @@ +import { screen, themeVars } from '@aics-client/design-system/styles'; +import { style } from '@vanilla-extract/css'; + +const formWrapper = style({ + display: 'flex', + flexDirection: 'column', + margin: '0 auto', + gap: themeVars.spacing.xl, + width: '60%', +}); + +const newPasswordWrapper = style({ + display: 'grid', + gap: themeVars.spacing.xl, + ...screen.lg({ + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + gap: themeVars.spacing.md, + }), +}); + +const inputField = style({ + width: '100%', +}); + +export { formWrapper, newPasswordWrapper, inputField }; diff --git a/apps/community/src/components/my/change-password/change-password-form.tsx b/apps/community/src/components/my/change-password/change-password-form.tsx new file mode 100644 index 0000000..ab76d65 --- /dev/null +++ b/apps/community/src/components/my/change-password/change-password-form.tsx @@ -0,0 +1,89 @@ +'use client'; + +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; + +import { Button, Input } from '@aics-client/design-system'; +import * as styles from '~/components/my/change-password/change-password-form.css'; + +const changePasswordSchema = z + .object({ + currentPassword: z + .string() + .min(1, { message: '현재 비밀번호를 입력해주세요.' }), + newPassword: z + .string() + .min(8, { message: '비밀번호는 최소 8자 이상이어야 합니다.' }) + .regex( + /^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[~!@#$%^&*])[a-zA-Z0-9~!@#$%^&*]{8,15}$/, + { message: '비밀번호는 영문, 숫자, 특수문자를 포함해야 합니다.' }, + ), + confirmNewPassword: z + .string() + .min(1, { message: '새 비밀번호 확인을 입력해주세요.' }), + }) + .refine((data) => data.newPassword === data.confirmNewPassword, { + message: '비밀번호가 일치하지 않습니다.', + path: ['confirmNewPassword'], + }); + +const defaultValues = { + currentPassword: '', + newPassword: '', + confirmNewPassword: '', +}; + +const ChangePasswordForm = () => { + const { + register, + handleSubmit, + formState: { errors, isValid }, + } = useForm>({ + resolver: zodResolver(changePasswordSchema), + mode: 'onChange', + defaultValues, + }); + + const onSubmit = (data: z.infer) => { + // TODO: 비밀번호 변경 API 호출 + console.log(data); + }; + + return ( +
+ + +
+ + +
+ + +
+ ); +}; + +export default ChangePasswordForm; diff --git a/packages/design-system/src/components/button/button.css.ts b/packages/design-system/src/components/button/button.css.ts index 15e3d5d..9869020 100644 --- a/packages/design-system/src/components/button/button.css.ts +++ b/packages/design-system/src/components/button/button.css.ts @@ -47,6 +47,7 @@ const buttonVariants = recipe({ disabled: { true: { cursor: 'default', + opacity: themeVars.opacity[75], }, false: { cursor: 'pointer',