1+ 'use client' ;
2+
3+ import { useState } from 'react' ;
4+ import Image from 'next/image' ;
5+ import Link from 'next/link' ;
6+ import { useRouter } from 'next/navigation' ;
7+ import { signIn } from 'next-auth/react' ;
8+ import { UserIcon , LockIcon , MailIcon , Loader2 , PhoneIcon } from 'lucide-react' ;
9+
10+ /**
11+ * Register page component that handles new user registration
12+ */
13+ export default function Register ( ) {
14+ const router = useRouter ( ) ;
15+ const [ error , setError ] = useState ( '' ) ;
16+ const [ success , setSuccess ] = useState ( '' ) ;
17+ const [ isLoading , setIsLoading ] = useState ( false ) ;
18+ const [ formData , setFormData ] = useState ( {
19+ name : '' ,
20+ email : '' ,
21+ password : '' ,
22+ confirmPassword : '' ,
23+ phoneNumber : '' ,
24+ } ) ;
25+
26+ // Handle input changes
27+ const handleInputChange = ( e ) => {
28+ const { name, value } = e . target ;
29+ setFormData ( ( prev ) => ( {
30+ ...prev ,
31+ [ name ] : value ,
32+ } ) ) ;
33+ setError ( '' ) ;
34+ } ;
35+
36+ // Handle form submission
37+ const handleRegister = async ( e ) => {
38+ e . preventDefault ( ) ;
39+ setIsLoading ( true ) ;
40+ setError ( '' ) ;
41+ setSuccess ( '' ) ;
42+
43+ // Validate form inputs
44+ if ( formData . password !== formData . confirmPassword ) {
45+ setError ( 'Passwords do not match.' ) ;
46+ setIsLoading ( false ) ;
47+ return ;
48+ }
49+
50+ if ( formData . password . length < 6 ) {
51+ setError ( 'Password must be at least 6 characters long.' ) ;
52+ setIsLoading ( false ) ;
53+ return ;
54+ }
55+
56+ if ( ! formData . email . match ( / ^ [ ^ @ \s ] + @ [ ^ @ \s ] + \. [ ^ @ \s ] + $ / ) ) {
57+ setError ( 'Please enter a valid email address.' ) ;
58+ setIsLoading ( false ) ;
59+ return ;
60+ }
61+
62+ if ( ! formData . name . trim ( ) ) {
63+ setError ( 'Please enter your name.' ) ;
64+ setIsLoading ( false ) ;
65+ return ;
66+ }
67+
68+ try {
69+ // 构建用户对象 - 确保与后端模型字段完全匹配
70+ const userObject = {
71+ type : 'BasicUser' ,
72+ Name : formData . name ,
73+ Email : formData . email ,
74+ PasswordHash : formData . password ,
75+ PhoneNumber : formData . phoneNumber || undefined // 不发送null值,而是完全省略
76+ } ;
77+
78+ // 如果是BasicUser类型,添加Image属性
79+ userObject . Image = '/default-avatar.png' ;
80+
81+ console . log ( 'Sending registration request with data:' , JSON . stringify ( userObject , null , 2 ) ) ;
82+
83+ const response = await fetch ( `${ process . env . NEXT_PUBLIC_AUTH_API_URL } /user/register` , {
84+ method : 'POST' ,
85+ headers : {
86+ 'Content-Type' : 'application/json' ,
87+ 'Accept' : 'application/json'
88+ } ,
89+ body : JSON . stringify ( userObject ) ,
90+ mode : 'cors' ,
91+ credentials : 'include' ,
92+ } ) ;
93+
94+ console . log ( 'Registration response status:' , response . status ) ;
95+
96+ // 尝试读取响应,无论成功或失败
97+ let responseText ;
98+ try {
99+ responseText = await response . text ( ) ;
100+ console . log ( 'Registration response body:' , responseText ) ;
101+ } catch ( error ) {
102+ console . error ( 'Error reading response:' , error ) ;
103+ }
104+
105+ if ( response . ok ) {
106+ setSuccess ( 'Registration successful! You can now log in.' ) ;
107+ // Redirect to login page after 2 seconds
108+ setTimeout ( ( ) => {
109+ router . push ( '/users/Login' ) ;
110+ } , 2000 ) ;
111+ } else {
112+ // 尝试解析JSON,如果失败则使用原始文本
113+ let errorMessage = 'Registration failed. Please try again.' ;
114+ if ( responseText ) {
115+ try {
116+ const errorData = JSON . parse ( responseText ) ;
117+ if ( typeof errorData === 'string' ) {
118+ errorMessage = errorData ;
119+ } else if ( errorData && typeof errorData === 'object' ) {
120+ // 尝试提取各种可能的错误消息字段
121+ errorMessage = errorData . message ||
122+ errorData . title ||
123+ errorData . error ||
124+ errorData . errors ?. join ( ', ' ) ||
125+ ( errorData . errors && typeof errorData . errors === 'object' ?
126+ Object . values ( errorData . errors ) . flat ( ) . join ( ', ' ) :
127+ JSON . stringify ( errorData ) ) ;
128+ }
129+ } catch ( e ) {
130+ // 如果不是有效的JSON,使用原始文本
131+ errorMessage = responseText ;
132+ }
133+ }
134+ setError ( errorMessage ) ;
135+ }
136+ } catch ( error ) {
137+ console . error ( 'Registration error:' , error ) ;
138+ setError ( 'An error occurred during registration. Please try again.' ) ;
139+ } finally {
140+ setIsLoading ( false ) ;
141+ }
142+ } ;
143+
144+ return (
145+ < main className = "min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-100 to-slate-200" >
146+ < div className = "w-full max-w-6xl mx-4 bg-white rounded-2xl shadow-xl overflow-hidden" >
147+ < div className = "grid md:grid-cols-2 gap-0" >
148+ { /* Left - Image Section */ }
149+ < div className = "relative h-48 md:h-auto" >
150+ < Image
151+ src = "/imgLogin/login_1.jpg"
152+ alt = "Registration background"
153+ fill
154+ priority
155+ className = "object-cover"
156+ sizes = "(max-width: 768px) 100vw, 50vw"
157+ />
158+ < div className = "absolute inset-0 bg-gradient-to-t from-black/50 to-transparent" />
159+ </ div >
160+
161+ { /* Right - Registration Form Section */ }
162+ < div className = "p-6 md:p-12 space-y-6" >
163+ { /* Header */ }
164+ < div className = "text-center space-y-2" >
165+ < h1 className = "text-2xl md:text-3xl font-bold text-gray-900" >
166+ Create an Account
167+ </ h1 >
168+ < p className = "text-slate-600" >
169+ Join us and enjoy our services
170+ </ p >
171+ </ div >
172+
173+ { /* Registration Form */ }
174+ < form className = "space-y-4" onSubmit = { handleRegister } >
175+ < div className = "space-y-4" >
176+ { /* Name Input */ }
177+ < div className = "relative" >
178+ < UserIcon className = "absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" />
179+ < input
180+ type = "text"
181+ name = "name"
182+ value = { formData . name }
183+ onChange = { handleInputChange }
184+ className = "w-full pl-10 pr-4 py-2.5 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary focus:border-transparent placeholder:text-gray-500"
185+ placeholder = "Full Name"
186+ disabled = { isLoading }
187+ required
188+ />
189+ </ div >
190+
191+ { /* Email Input */ }
192+ < div className = "relative" >
193+ < MailIcon className = "absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" />
194+ < input
195+ type = "email"
196+ name = "email"
197+ value = { formData . email }
198+ onChange = { handleInputChange }
199+ className = "w-full pl-10 pr-4 py-2.5 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary focus:border-transparent placeholder:text-gray-500"
200+ placeholder = "Email"
201+ disabled = { isLoading }
202+ required
203+ />
204+ </ div >
205+
206+ { /* Phone Input (Optional) */ }
207+ < div className = "relative" >
208+ < PhoneIcon className = "absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" />
209+ < input
210+ type = "tel"
211+ name = "phoneNumber"
212+ value = { formData . phoneNumber }
213+ onChange = { handleInputChange }
214+ className = "w-full pl-10 pr-4 py-2.5 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary focus:border-transparent placeholder:text-gray-500"
215+ placeholder = "Phone Number (Optional)"
216+ disabled = { isLoading }
217+ />
218+ </ div >
219+
220+ { /* Password Input */ }
221+ < div className = "relative" >
222+ < LockIcon className = "absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" />
223+ < input
224+ type = "password"
225+ name = "password"
226+ value = { formData . password }
227+ onChange = { handleInputChange }
228+ className = "w-full pl-10 pr-4 py-2.5 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary focus:border-transparent placeholder:text-gray-500"
229+ placeholder = "Password"
230+ disabled = { isLoading }
231+ required
232+ minLength = { 6 }
233+ />
234+ </ div >
235+
236+ { /* Confirm Password Input */ }
237+ < div className = "relative" >
238+ < LockIcon className = "absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" />
239+ < input
240+ type = "password"
241+ name = "confirmPassword"
242+ value = { formData . confirmPassword }
243+ onChange = { handleInputChange }
244+ className = "w-full pl-10 pr-4 py-2.5 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary focus:border-transparent placeholder:text-gray-500"
245+ placeholder = "Confirm Password"
246+ disabled = { isLoading }
247+ required
248+ minLength = { 6 }
249+ />
250+ </ div >
251+ </ div >
252+
253+ { /* Error Message */ }
254+ { error && (
255+ < div className = "bg-red-50 border border-red-200 text-red-600 px-4 py-3 rounded-lg text-sm" >
256+ { typeof error === 'string' ? error : 'An error occurred during registration. Please try again.' }
257+ </ div >
258+ ) }
259+
260+ { /* Success Message */ }
261+ { success && (
262+ < div className = "bg-green-50 border border-green-200 text-green-600 px-4 py-3 rounded-lg text-sm" >
263+ { success }
264+ </ div >
265+ ) }
266+
267+ { /* Register Button */ }
268+ < button
269+ type = "submit"
270+ disabled = { isLoading }
271+ className = "w-full bg-primary hover:bg-primary/90 text-white font-semibold py-2.5 rounded-lg transition focus:ring-2 focus:ring-offset-2 focus:ring-primary disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center space-x-2"
272+ >
273+ { isLoading ? (
274+ < >
275+ < Loader2 className = "animate-spin h-5 w-5" />
276+ < span > Creating account...</ span >
277+ </ >
278+ ) : (
279+ < span > Create Account</ span >
280+ ) }
281+ </ button >
282+ </ form >
283+
284+ { /* Sign In Link */ }
285+ < p className = "text-center text-gray-600 text-sm" >
286+ Already have an account?{ ' ' }
287+ < Link
288+ href = "/users/Login"
289+ className = "text-primary hover:text-primary/80 font-semibold transition-colors"
290+ >
291+ Sign in
292+ </ Link >
293+ </ p >
294+ </ div >
295+ </ div >
296+ </ div >
297+ </ main >
298+ ) ;
299+ }
0 commit comments