'use client'; import { useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; import { useFormState } from 'react-dom'; import { useToast } from '@/hooks/use-toast'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Ship, Eye, EyeOff, CheckCircle, XCircle, AlertCircle, Shield } from 'lucide-react'; import Link from 'next/link'; import SuccessPage from './SuccessPage'; import { PasswordPolicy } from '@/lib/users/auth/passwordUtil'; import { PasswordValidationResult, resetPasswordAction, validatePasswordAction } from '@/lib/users/auth/partners-auth'; interface PasswordRequirement { text: string; met: boolean; type: 'length' | 'uppercase' | 'lowercase' | 'number' | 'symbol' | 'pattern'; } interface Props { token: string; userId: number; passwordPolicy: PasswordPolicy; } export default function ResetPasswordForm({ token, userId, passwordPolicy }: Props) { const router = useRouter(); const { toast } = useToast(); // 상태 관리 const [showPassword, setShowPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); const [newPassword, setNewPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [passwordValidation, setPasswordValidation] = useState(null); const [isValidatingPassword, setIsValidatingPassword] = useState(false); // 서버 액션 상태 const [resetState, resetAction] = useFormState(resetPasswordAction, { success: false, error: undefined, message: undefined, }); // 패스워드 검증 (디바운싱 적용) useEffect(() => { const validatePassword = async () => { if (!newPassword) { setPasswordValidation(null); return; } setIsValidatingPassword(true); try { // 사용자 ID를 포함한 검증 (히스토리 체크 포함) const validation = await validatePasswordAction(newPassword, userId); setPasswordValidation(validation); } catch (error) { console.error('Password validation error:', error); setPasswordValidation(null); } finally { setIsValidatingPassword(false); } }; // 디바운싱: 500ms 후에 검증 실행 const timeoutId = setTimeout(validatePassword, 500); return () => clearTimeout(timeoutId); }, [newPassword, userId]); // 서버 액션 결과 처리 useEffect(() => { if (resetState.error) { toast({ title: '오류', description: resetState.error, variant: 'destructive', }); } }, [resetState, toast]); // 패스워드 요구사항 생성 const getPasswordRequirements = (): PasswordRequirement[] => { if (!passwordValidation) return []; const { strength } = passwordValidation; const requirements: PasswordRequirement[] = [ { text: `${passwordPolicy.minLength}자 이상`, met: strength.length >= passwordPolicy.minLength, type: 'length' } ]; if (passwordPolicy.requireUppercase) { requirements.push({ text: '대문자 포함', met: strength.hasUppercase, type: 'uppercase' }); } if (passwordPolicy.requireLowercase) { requirements.push({ text: '소문자 포함', met: strength.hasLowercase, type: 'lowercase' }); } if (passwordPolicy.requireNumbers) { requirements.push({ text: '숫자 포함', met: strength.hasNumbers, type: 'number' }); } if (passwordPolicy.requireSymbols) { requirements.push({ text: '특수문자 포함', met: strength.hasSymbols, type: 'symbol' }); } return requirements; }; // 패스워드 강도 색상 const getStrengthColor = (score: number) => { switch (score) { case 1: return 'text-red-600'; case 2: return 'text-orange-600'; case 3: return 'text-yellow-600'; case 4: return 'text-blue-600'; case 5: return 'text-green-600'; default: return 'text-gray-600'; } }; const getStrengthText = (score: number) => { switch (score) { case 1: return '매우 약함'; case 2: return '약함'; case 3: return '보통'; case 4: return '강함'; case 5: return '매우 강함'; default: return ''; } }; const passwordRequirements = getPasswordRequirements(); const allRequirementsMet = passwordValidation?.policyValid && passwordValidation?.historyValid !== false; const passwordsMatch = newPassword === confirmPassword && confirmPassword.length > 0; const canSubmit = allRequirementsMet && passwordsMatch && !isValidatingPassword; // 성공 화면 if (resetState.success) { return ; } return (
eVCP
새 비밀번호 설정 계정 보안을 위해 강력한 비밀번호를 설정해주세요.
{/* 새 비밀번호 */}
setNewPassword(e.target.value)} placeholder="새 비밀번호를 입력하세요" required />
{/* 패스워드 강도 표시 */} {passwordValidation && (
강도: {getStrengthText(passwordValidation.strength.score)} {isValidatingPassword && (
)}
{/* 강도 진행바 */}
)} {/* 패스워드 요구사항 */} {passwordRequirements.length > 0 && (
{passwordRequirements.map((req, index) => (
{req.met ? ( ) : ( )} {req.text}
))}
)} {/* 히스토리 검증 결과 */} {passwordValidation?.historyValid === false && (
최근 {passwordPolicy.historyCount}개 비밀번호와 달라야 합니다
)} {/* 추가 피드백 */} {passwordValidation?.strength.feedback && passwordValidation.strength.feedback.length > 0 && (
{passwordValidation.strength.feedback.map((feedback, index) => (
{feedback}
))}
)} {/* 정책 오류 */} {passwordValidation && !passwordValidation.policyValid && passwordValidation.policyErrors.length > 0 && (
{passwordValidation.policyErrors.map((error, index) => (
{error}
))}
)}
{/* 비밀번호 확인 */}
setConfirmPassword(e.target.value)} placeholder="비밀번호를 다시 입력하세요" required />
{/* 비밀번호 일치 확인 */} {confirmPassword && (
{passwordsMatch ? ( <> 비밀번호가 일치합니다 ) : ( <> 비밀번호가 일치하지 않습니다 )}
)}
로그인 페이지로 돌아가기
); }