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