From adbe3b8bc699bc5bfffda74e6f20c36e950c0500 Mon Sep 17 00:00:00 2001 From: hye-inA Date: Fri, 16 Jan 2026 11:47:40 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat=20:=20SignupForm=20UI=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#144)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domains/auth/components/SignupForm.jsx | 237 ++++++++++++++++----- 1 file changed, 183 insertions(+), 54 deletions(-) diff --git a/src/domains/auth/components/SignupForm.jsx b/src/domains/auth/components/SignupForm.jsx index 8a6c5c2..bfb67f1 100644 --- a/src/domains/auth/components/SignupForm.jsx +++ b/src/domains/auth/components/SignupForm.jsx @@ -1,5 +1,4 @@ import { useState } from 'react'; -import { useNavigate } from 'react-router-dom'; import { Box, TextField, @@ -10,110 +9,240 @@ import { IconButton, CircularProgress, Link, + Divider, + LinearProgress, } from '@mui/material'; import { Email as EmailIcon, Lock as LockIcon, Visibility, VisibilityOff, + Check as CheckIcon, + Close as CloseIcon, } from '@mui/icons-material'; import { useAuth } from '../../../contexts/AuthContext'; -export default function LoginForm({ onSwitchToSignUp }) { - const navigate = useNavigate(); - const { login } = useAuth(); +export default function SignupForm({ onSwitchToLogin }) { + const { register } = useAuth() - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [showPassword, setShowPassword] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(''); + const [formData, setFormData] = useState({ + email: '', + password: '', + confirmPassword: '', + }) + const [showPassword, setShowPassword] = useState(false) + const [showConfirmPassword, setShowConfirmPassword] = useState(false) + const [error, setError] = useState('') + const [success, setSuccess] = useState('') + const [isLoading, setIsLoading] = useState(false) + + const handleChange = (e) => { + const { name, value } = e.target + setFormData((prev) => ({ ...prev, [name]: value })) + setError('') + } const handleSubmit = async (e) => { - e.preventDefault(); - setError(''); + e.preventDefault() + setError('') + setSuccess('') + setIsLoading(true) - if (!email || !password) { - setError('이메일과 비밀번호를 입력해주세요.'); - return; - } + // TODO : 유효성 검사 추가 - setIsLoading(true); - const result = await login(email, password); + try { + const result = await register(formData.email, formData.password) - if (result.success) { - navigate('/dashboard'); - } else { - setError(result.message); + if (result.success) { + setSuccess(result.message) + setStep('verify') + } else { + setError(result.message) + } + } catch (err) { + setError('회원가입 중 오류가 발생했습니다.') + } finally { + setIsLoading(false) } - setIsLoading(false); - }; + } + return ( - - - 로그인 - - - AI 언어 학습 시스템에 오신 것을 환영합니다 - + + {/* 타이틀 */} + + + 회원가입 + + + 새로운 계정을 만들어보세요 + + - {error && {error}} + {/* 에러/성공 메시지 */} + {error && ( + + {error} + + )} + {success && ( + + {success} + + )} + {/* 이메일 입력 */} setEmail(e.target.value)} + label="이메일" + placeholder="example@email.com" + value={formData.email} + onChange={handleChange} disabled={isLoading} - sx={{ mb: 2 }} InputProps={{ startAdornment: ( - + + + ), }} /> + {/* 비밀번호 입력 */} + + + + + ), + endAdornment: ( + + setShowPassword(!showPassword)} + edge="end" + size="small" + > + {showPassword ? : } + + + ), + }} + /> + + {/* TODO : 비밀번호 강도 표시 추가 */} + + + {/* 비밀번호 확인 */} setPassword(e.target.value)} + name="confirmPassword" + type={showConfirmPassword ? 'text' : 'password'} + label="비밀번호 확인" + placeholder="비밀번호를 다시 입력하세요" + value={formData.confirmPassword} + onChange={handleChange} disabled={isLoading} - sx={{ mb: 3 }} + error={formData.confirmPassword && formData.password !== formData.confirmPassword} + helperText={ + formData.confirmPassword && formData.password !== formData.confirmPassword + ? '비밀번호가 일치하지 않습니다' + : '' + } InputProps={{ startAdornment: ( - + + + ), endAdornment: ( - setShowPassword(!showPassword)}> - {showPassword ? : } + setShowConfirmPassword(!showConfirmPassword)} + edge="end" + size="small" + > + {showConfirmPassword ? : } ), }} /> + {/* 가입 버튼 */} - - 계정이 없으신가요?{' '} - - 회원가입 - - + {/* 구분선 */} + + + 또는 + + + + {/* 로그인 안내 */} + + + 이미 계정이 있으신가요?{' '} + + 로그인 + + + - ); + ) } \ No newline at end of file From a59dd83c3a24f5081bb9eb4b1e8e3aa5b6882ccd Mon Sep 17 00:00:00 2001 From: hye-inA Date: Fri, 16 Jan 2026 12:46:49 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat=20:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=ED=95=84=EB=93=9C=20=EA=B0=92=20=EC=9C=A0=ED=9A=A8?= =?UTF-8?q?=EC=84=B1=20=EA=B2=80=EC=82=AC=20UI=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domains/auth/components/SignupForm.jsx | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/domains/auth/components/SignupForm.jsx b/src/domains/auth/components/SignupForm.jsx index bfb67f1..ca7c07d 100644 --- a/src/domains/auth/components/SignupForm.jsx +++ b/src/domains/auth/components/SignupForm.jsx @@ -42,13 +42,31 @@ export default function SignupForm({ onSwitchToLogin }) { setError('') } + const isPasswordValid = passwordStrength >= 4 + const handleSubmit = async (e) => { e.preventDefault() setError('') setSuccess('') setIsLoading(true) - // TODO : 유효성 검사 추가 + // 유효성 검사 + if (!formData.email || !formData.password || !formData.confirmPassword) { + setError('모든 필드를 입력해주세요.') + setIsLoading(false) + return + } + if (!isPasswordValid) { + setError('비밀번호 조건을 충족해주세요.') + setIsLoading(false) + return + } + + if (formData.password !== formData.confirmPassword) { + setError('비밀번호가 일치하지 않습니다.') + setIsLoading(false) + return + } try { const result = await register(formData.email, formData.password) @@ -245,4 +263,4 @@ export default function SignupForm({ onSwitchToLogin }) { ) -} \ No newline at end of file +}