"use client"; import { useState, useEffect } from "react"; import { useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { LanguageSelect } from "@/components/LanguageSelect"; import { ThemeSelect } from "@/components/ThemeSelect"; import { companyInfoSchema, userInfoSchema, planSelectionSchema } from "@/lib/validations/auth"; import { ArrowLeft, Building2, User, Mail, Phone, Lock, Briefcase, Users, FileText } from "lucide-react"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; export function SignupPage() { const router = useRouter(); const t = useTranslations("auth"); const tSignup = useTranslations("signup"); const tValidation = useTranslations("validation"); const [step, setStep] = useState(1); const [formData, setFormData] = useState({ // 회사 정보 companyName: "", businessNumber: "", industry: "", companySize: "", // 담당자 정보 name: "", position: "", email: "", phone: "", userId: "", password: "", passwordConfirm: "", // 플랜 및 추천인 plan: "demo", salesCode: "", // 약관 agreeTerms: false, agreePrivacy: false, }); const handleInputChange = (field: string, value: string | boolean) => { setFormData(prev => ({ ...prev, [field]: value })); }; // 사업자등록번호 자동 포맷팅 (000-00-00000) const formatBusinessNumber = (value: string) => { // 숫자만 추출 const numbers = value.replace(/[^\d]/g, ''); // 최대 10자리까지만 const limited = numbers.slice(0, 10); // 형식에 맞게 하이픈 추가 if (limited.length <= 3) { return limited; } else if (limited.length <= 5) { return `${limited.slice(0, 3)}-${limited.slice(3)}`; } else { return `${limited.slice(0, 3)}-${limited.slice(3, 5)}-${limited.slice(5)}`; } }; const handleBusinessNumberChange = (value: string) => { const formatted = formatBusinessNumber(value); handleInputChange("businessNumber", formatted); }; // 핸드폰 번호 자동 포맷팅 (010-1111-1111 or 010-111-1111) const formatPhoneNumber = (value: string) => { // 숫자만 추출 const numbers = value.replace(/[^\d]/g, ''); // 최대 11자리까지만 const limited = numbers.slice(0, 11); // 형식에 맞게 하이픈 추가 if (limited.length <= 3) { return limited; } else if (limited.length <= 6) { return `${limited.slice(0, 3)}-${limited.slice(3)}`; } else if (limited.length === 10) { // 10자리: 010-111-1111 return `${limited.slice(0, 3)}-${limited.slice(3, 6)}-${limited.slice(6)}`; } else { // 11자리: 010-1111-1111 return `${limited.slice(0, 3)}-${limited.slice(3, 7)}-${limited.slice(7)}`; } }; const handlePhoneNumberChange = (value: string) => { const formatted = formatPhoneNumber(value); handleInputChange("phone", formatted); }; // 전체 동의 처리 const handleAgreeAll = (checked: boolean) => { setFormData(prev => ({ ...prev, agreeTerms: checked, agreePrivacy: checked, })); }; // 전체 동의 체크박스 상태 계산 const isAllAgreed = formData.agreeTerms && formData.agreePrivacy; const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(""); const [isChecking, setIsChecking] = useState(true); // 이미 로그인된 상태인지 확인 (페이지 로드 시, 뒤로가기 시) useEffect(() => { const checkAuth = async () => { try { // 🔵 Next.js 내부 API - 쿠키에서 토큰 확인 (PHP 호출 X, 성능 최적화) const response = await fetch('/api/auth/check'); if (response.ok) { // 이미 로그인됨 → 대시보드로 리다이렉트 (replace로 히스토리에서 제거) router.replace('/dashboard'); return; } // 인증 안됨 (401) → 현재 페이지 유지 } catch { // API 호출 실패 → 현재 페이지 유지 } finally { setIsChecking(false); } }; checkAuth(); }, [router]); const handleSubmit = async () => { setIsLoading(true); setError(""); try { // Prepare request body matching backend format const requestBody = { user_id: formData.userId, name: formData.name, email: formData.email, phone: formData.phone, password: formData.password, password_confirmation: formData.passwordConfirm, position: formData.position || "", company_name: formData.companyName, business_num: formData.businessNumber, company_scale: formData.companySize, industry: formData.industry, }; // 🔵 Next.js 프록시 → PHP /api/v1/register (회원가입 처리) const response = await fetch('/api/auth/signup', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(requestBody), credentials: 'include', }); const data = await response.json(); if (!response.ok) { setError(data.error || 'Registration failed'); setIsLoading(false); return; } console.log('✅ Signup successful:', data); // Navigate to login page after successful signup router.push("/login?registered=true"); } catch (err) { console.error('Signup error:', err); setError('Network error. Please try again.'); setIsLoading(false); } }; const [stepErrors, setStepErrors] = useState<{ [key: string]: string }>({}); const validateStep1 = () => { const result = companyInfoSchema.safeParse({ companyName: formData.companyName, businessNumber: formData.businessNumber, industry: formData.industry, companySize: formData.companySize, }); if (!result.success) { const firstError = result.error.issues[0]; setStepErrors({ step1: firstError.message }); return false; } setStepErrors({}); return true; }; const validateStep2 = () => { const result = userInfoSchema.safeParse({ name: formData.name, position: formData.position, email: formData.email, phone: formData.phone, userId: formData.userId, password: formData.password, passwordConfirm: formData.passwordConfirm, }); if (!result.success) { const firstError = result.error.issues[0]; setStepErrors({ step2: firstError.message }); return false; } setStepErrors({}); return true; }; const validateStep3 = () => { const result = planSelectionSchema.safeParse({ plan: formData.plan, salesCode: formData.salesCode, agreeTerms: formData.agreeTerms, agreePrivacy: formData.agreePrivacy, }); if (!result.success) { const firstError = result.error.issues[0]; setStepErrors({ step3: firstError.message }); return false; } setStepErrors({}); return true; }; const isStep1Valid = formData.companyName && formData.businessNumber && formData.industry && formData.companySize; const isStep2Valid = formData.name && formData.email && formData.phone && formData.userId && formData.password && formData.password === formData.passwordConfirm; const isStep3Valid = formData.agreeTerms && formData.agreePrivacy; // 인증 체크 중일 때는 로딩 표시 if (isChecking) { return (

불러오는 중...

); } return (
{/* Header */}
{/* Main Content */}
{/* Progress Steps */}
{[1, 2, 3].map((stepNumber) => (
{(stepNumber === 2 || stepNumber === 3) && (
stepNumber - 1 ? "bg-primary" : "bg-muted" }`} /> )}
= stepNumber ? "bg-primary text-white" : "bg-muted text-muted-foreground" }`}> {stepNumber}
{(stepNumber === 1 || stepNumber === 2) && (
stepNumber ? "bg-primary" : "bg-muted" }`} /> )}
))}
= 1 ? "text-foreground font-medium" : "text-muted-foreground"}> {t("companyInfo")} = 2 ? "text-foreground font-medium" : "text-muted-foreground"}> {t("userInfo")} = 3 ? "text-foreground font-medium" : "text-muted-foreground"}> {t("planSelection")}
{/* Step 1: 회사 정보 */} {step === 1 && (

{t("step1Title")}

{t("step1Desc")}

handleInputChange("companyName", e.target.value)} className="clean-input" />
handleBusinessNumberChange(e.target.value)} className="clean-input" />
{stepErrors.step1 && (

{stepErrors.step1}

)}
)} {/* Step 2: 담당자 정보 */} {step === 2 && (

{t("step2Title")}

{t("step2Desc")}

{ e.preventDefault(); if (validateStep2()) setStep(3); }} className="space-y-4">
handleInputChange("name", e.target.value)} className="clean-input" />
handleInputChange("position", e.target.value)} className="clean-input" />
handleInputChange("email", e.target.value)} className="clean-input" />
handlePhoneNumberChange(e.target.value)} className="clean-input" />
handleInputChange("userId", e.target.value)} className="clean-input" />
handleInputChange("password", e.target.value)} className="clean-input" />
handleInputChange("passwordConfirm", e.target.value)} className="clean-input" /> {formData.passwordConfirm && formData.password !== formData.passwordConfirm && (

{tValidation("passwordMismatch")}

)}
{stepErrors.step2 && (

{stepErrors.step2}

)}
)} {/* Step 3: 플랜 선택 */} {step === 3 && (
{/*

플랜을 선택해주세요

먼저 30일 무료 체험으로 시작해보세요

{[ { id: "demo", name: "데모 체험판", desc: "30일 무료 체험 (모든 기능 이용)", badge: "추천" }, { id: "standard", name: "스탠다드", desc: "중소기업 최적화 플랜" }, { id: "premium", name: "프리미엄", desc: "중견기업 맞춤형 플랜" }, ].map((plan) => ( ))}
handleSalesCodeChange(e.target.value)} className={`clean-input pr-10 ${ salesCodeValid === true ? "border-green-500" : salesCodeValid === false ? "border-destructive" : "" }`} /> {salesCodeValid === true && ( )}
{salesCodeValid === true && (

유효한 코드입니다! {discount}% 할인이 적용됩니다

)} {salesCodeValid === false && (

유효하지 않은 코드입니다

)}

💡 예시 코드: SALES2024 (20%), PARTNER30 (30%), VIP50 (50%)

*/}
{/* 전체 동의 */} {/* 개별 약관 */}
{error && (

{error}

)}
)} {/* Login Link */}

{t("alreadyHaveAccount")}{" "}

); }