1
+ // app/profile/UserProfileContent.tsx
2
+ "use client" ;
3
+
4
+ import React , { useState , useEffect } from "react" ;
5
+ import { useAuth } from "@/app/context/AuthContext" ;
6
+ import {
7
+ Card ,
8
+ CardContent ,
9
+ CardHeader ,
10
+ CardTitle ,
11
+ CardDescription ,
12
+ CardFooter ,
13
+ } from "@/components/ui/card" ;
14
+ import { Button } from "@/components/ui/button" ;
15
+ import { Tabs , TabsContent , TabsList , TabsTrigger } from "@/components/ui/tabs" ;
16
+ import BadgeDisplay from "@/components/BadgeDisplay" ;
17
+ import AuthenticationDialog from "@/components/AuthenticationDialog" ;
18
+ import UserProjectRequests from "@/components/UserProjectRequests" ;
19
+ import { useRouter , useSearchParams } from "next/navigation" ;
20
+ import {
21
+ Award ,
22
+ Star ,
23
+ MessageSquare ,
24
+ User ,
25
+ Settings ,
26
+ FileText ,
27
+ } from "lucide-react" ;
28
+ import ProfileForm from "@/components/ProfileForm" ;
29
+
30
+ interface ProfileStats {
31
+ ratings : number ;
32
+ comments : number ;
33
+ submissions : number ;
34
+ }
35
+
36
+ const UserProfileContent = ( ) => {
37
+ const { user, isAuthenticated } = useAuth ( ) ;
38
+ const router = useRouter ( ) ;
39
+ const searchParams = useSearchParams ( ) ;
40
+ const tabParam = searchParams . get ( 'tab' ) ;
41
+
42
+ const [ showAuthDialog , setShowAuthDialog ] = useState ( false ) ;
43
+ const [ stats , setStats ] = useState < ProfileStats > ( {
44
+ ratings : 0 ,
45
+ comments : 0 ,
46
+ submissions : 0 ,
47
+ } ) ;
48
+ const [ activeTab , setActiveTab ] = useState < string > ( "profile" ) ;
49
+
50
+ useEffect ( ( ) => {
51
+ if ( ! isAuthenticated ) {
52
+ setShowAuthDialog ( true ) ;
53
+ } else if ( user ) {
54
+ // Set active tab from URL parameter if present
55
+ if ( tabParam && [ 'profile' , 'my-requests' ] . includes ( tabParam ) ) {
56
+ setActiveTab ( tabParam ) ;
57
+ }
58
+
59
+ // Fetch user stats
60
+ const fetchStats = async ( ) => {
61
+ try {
62
+ const [ ratingsRes , commentsRes ] = await Promise . all ( [
63
+ fetch ( `/api/ratings?userId=${ user . id } ` ) ,
64
+ fetch ( `/api/comments?userId=${ user . id } ` ) ,
65
+ ] ) ;
66
+
67
+ const ratings = await ratingsRes . json ( ) ;
68
+ const comments = await commentsRes . json ( ) ;
69
+
70
+ setStats ( {
71
+ ratings : ratings . length ,
72
+ comments : comments . length ,
73
+ submissions : 0 , // Will be updated when we fetch project requests
74
+ } ) ;
75
+ } catch ( error ) {
76
+ console . error ( "Error fetching user stats:" , error ) ;
77
+ }
78
+ } ;
79
+
80
+ fetchStats ( ) ;
81
+ }
82
+ } , [ isAuthenticated , user , tabParam ] ) ;
83
+
84
+ if ( ! isAuthenticated ) {
85
+ return (
86
+ < div className = "min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 py-12 mt-10" >
87
+ < div className = "container mx-auto px-4" >
88
+ < div className = "max-w-md mx-auto bg-slate-800/50 border border-slate-700 rounded-lg p-8 text-center" >
89
+ < h1 className = "text-2xl font-bold text-white mb-4" > Profile</ h1 >
90
+ < p className = "text-gray-400 mb-6" >
91
+ You need to be signed in to view your profile.
92
+ </ p >
93
+ < Button
94
+ onClick = { ( ) => setShowAuthDialog ( true ) }
95
+ className = "bg-purple-500 hover:bg-purple-600 text-white"
96
+ >
97
+ Sign In
98
+ </ Button >
99
+
100
+ < AuthenticationDialog
101
+ isOpen = { showAuthDialog }
102
+ onOpenChange = { setShowAuthDialog }
103
+ />
104
+ </ div >
105
+ </ div >
106
+ </ div >
107
+ ) ;
108
+ }
109
+
110
+ if ( ! user ) return null ;
111
+
112
+ const levelProgress = user . points % 100 ;
113
+ const pointsToNextLevel = 100 - levelProgress ;
114
+
115
+ return (
116
+ < div className = "min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 py-12 mt-10" >
117
+ < div className = "container mx-auto px-4" >
118
+ < div className = "max-w-5xl mx-auto" >
119
+ < h1 className = "text-3xl font-bold text-white text-center mb-8" >
120
+ Your Profile
121
+ </ h1 >
122
+
123
+ < div className = "grid grid-cols-1 lg:grid-cols-4 gap-8" >
124
+ { /* Sidebar */ }
125
+ < div className = "lg:col-span-1" >
126
+ < Card className = "bg-slate-800/50 border-slate-700 text-white h-fit" >
127
+ < CardHeader className = "pb-4" >
128
+ < CardTitle className = "text-xl" >
129
+ < User className = "inline-block mr-2 text-purple-400" />
130
+ { user . displayName }
131
+ </ CardTitle >
132
+ < CardDescription className = "text-gray-400" >
133
+ @{ user . username }
134
+ </ CardDescription >
135
+ </ CardHeader >
136
+ < CardContent className = "space-y-4" >
137
+ < div className = "flex flex-col items-center" >
138
+ < div className = "relative mb-3" >
139
+ { user . avatarUrl ? (
140
+ < img
141
+ src = { user . avatarUrl }
142
+ alt = { user . displayName }
143
+ className = "w-24 h-24 rounded-full"
144
+ />
145
+ ) : (
146
+ < div className = "w-24 h-24 rounded-full bg-slate-700 flex items-center justify-center" >
147
+ < User className = "h-12 w-12 text-slate-400" />
148
+ </ div >
149
+ ) }
150
+ </ div >
151
+
152
+ < div className = "flex items-center gap-2 mb-1" >
153
+ < Award className = "text-yellow-500" />
154
+ < span className = "font-bold" > Level { user . level } </ span >
155
+ </ div >
156
+
157
+ < div className = "text-sm text-gray-400 mb-2" >
158
+ { user . points } points ({ pointsToNextLevel } to Level{ " " }
159
+ { user . level + 1 } )
160
+ </ div >
161
+
162
+ < div className = "w-full bg-slate-700 rounded-full h-2 mb-4" >
163
+ < div
164
+ className = "bg-gradient-to-r from-purple-500 to-blue-500 h-2 rounded-full"
165
+ style = { { width : `${ levelProgress } %` } }
166
+ > </ div >
167
+ </ div >
168
+ </ div >
169
+
170
+ < div className = "space-y-3" >
171
+ < div className = "flex justify-between items-center" >
172
+ < div className = "flex items-center gap-2" >
173
+ < Star className = "text-yellow-500 w-4 h-4" />
174
+ < span className = "text-sm" > Ratings</ span >
175
+ </ div >
176
+ < span className = "font-semibold" > { stats . ratings } </ span >
177
+ </ div >
178
+
179
+ < div className = "flex justify-between items-center" >
180
+ < div className = "flex items-center gap-2" >
181
+ < MessageSquare className = "text-blue-500 w-4 h-4" />
182
+ < span className = "text-sm" > Comments</ span >
183
+ </ div >
184
+ < span className = "font-semibold" > { stats . comments } </ span >
185
+ </ div >
186
+
187
+ < div className = "flex justify-between items-center" >
188
+ < div className = "flex items-center gap-2" >
189
+ < FileText className = "text-green-500 w-4 h-4" />
190
+ < span className = "text-sm" > Submissions</ span >
191
+ </ div >
192
+ < span className = "font-semibold" > { stats . submissions } </ span >
193
+ </ div >
194
+ </ div >
195
+ </ CardContent >
196
+ </ Card >
197
+
198
+ < Card className = "bg-slate-800/50 border-slate-700 text-white mt-6" >
199
+ < CardHeader >
200
+ < CardTitle className = "text-lg" > Your Badges</ CardTitle >
201
+ </ CardHeader >
202
+ < CardContent >
203
+ < BadgeDisplay userBadges = { user . badges } />
204
+ </ CardContent >
205
+ < CardFooter >
206
+ < Button
207
+ variant = "outline"
208
+ className = "w-full bg-slate-700 hover:bg-slate-600 border-slate-600 text-white"
209
+ onClick = { ( ) => router . push ( "/badges" ) }
210
+ >
211
+ View All Badges
212
+ </ Button >
213
+ </ CardFooter >
214
+ </ Card >
215
+ </ div >
216
+
217
+ { /* Main Content */ }
218
+ < div className = "lg:col-span-3" >
219
+ < Card className = "bg-slate-800/50 border-slate-700 text-white" >
220
+ < CardHeader >
221
+ < Tabs
222
+ defaultValue = { activeTab }
223
+ value = { activeTab }
224
+ onValueChange = { ( value ) => {
225
+ setActiveTab ( value ) ;
226
+ router . push ( `/profile?tab=${ value } ` ) ;
227
+ } }
228
+ >
229
+ < TabsList className = "bg-slate-700 w-full grid grid-cols-2" >
230
+ < TabsTrigger
231
+ value = "profile"
232
+ className = "data-[state=active]:bg-slate-900"
233
+ >
234
+ < Settings className = "h-4 w-4 mr-2" />
235
+ Profile Settings
236
+ </ TabsTrigger >
237
+ < TabsTrigger
238
+ value = "my-requests"
239
+ className = "data-[state=active]:bg-slate-900"
240
+ >
241
+ < FileText className = "h-4 w-4 mr-2" />
242
+ My Project Requests
243
+ </ TabsTrigger >
244
+ </ TabsList >
245
+
246
+ < TabsContent value = "profile" className = "pt-6" >
247
+ < ProfileForm />
248
+ </ TabsContent >
249
+
250
+ < TabsContent value = "my-requests" className = "pt-6" >
251
+ < UserProjectRequests />
252
+ </ TabsContent >
253
+ </ Tabs >
254
+ </ CardHeader >
255
+ </ Card >
256
+ </ div >
257
+ </ div >
258
+ </ div >
259
+ </ div >
260
+ </ div >
261
+ ) ;
262
+ } ;
263
+
264
+ export default UserProfileContent ;
0 commit comments