@@ -2,15 +2,19 @@ import {
2
2
SidebarInset ,
3
3
SidebarProvider ,
4
4
SidebarTrigger ,
5
- } from "@/components/ui/sidebar"
6
- import { SidebarLeft } from '@/components/Sidebar/Sidebar'
5
+ } from "@/components/ui/sidebar" ;
6
+ import { SidebarLeft } from "@/components/Sidebar/Sidebar" ;
7
+ import { StarIcon } from "@radix-ui/react-icons" ;
7
8
import { useEffect , useState } from "react" ;
8
9
import { useParams } from "react-router-dom" ;
9
- import { Skeleton } from '@/components/ui/skeleton' ;
10
- import axios from 'axios' ;
11
- import ReactMarkdown from 'react-markdown' ;
10
+ import { Skeleton } from "@/components/ui/skeleton" ;
11
+ import axios from "axios" ;
12
+ import ReactMarkdown from "react-markdown" ;
13
+ import { FaStar as FilledStarIcon } from "react-icons/fa" ;
14
+ import { Button } from "@/components/ui/button" ; // Import the Button component
15
+ import { toast } from "sonner" ; // Assuming you use this for notifications
12
16
13
- const backendUrl = import . meta. env . VITE_BACKEND_URL || ' http://localhost:5000' ;
17
+ const backendUrl = import . meta. env . VITE_BACKEND_URL || " http://localhost:5000" ;
14
18
15
19
interface Project {
16
20
projectId : string ;
@@ -27,13 +31,18 @@ const ProjectDisplay = () => {
27
31
28
32
const [ project , setProject ] = useState < Project | null > ( null ) ;
29
33
const [ loading , setLoading ] = useState ( true ) ;
34
+ const [ isStarred , setIsStarred ] = useState ( false ) ;
35
+ const [ starCount , setStarCount ] = useState ( 0 ) ; // Initialize as 0
36
+ const [ isStarRequestInProgress , setIsStarRequestInProgress ] = useState ( false ) ; // To disable button during request
30
37
31
38
useEffect ( ( ) => {
32
39
const fetchProject = async ( ) => {
33
40
try {
34
41
const response = await axios . get ( `${ backendUrl } /profile/${ username } /projects/${ projectId } ` ) ;
35
42
if ( response . status === 200 ) {
36
- setProject ( response . data ) ;
43
+ const projectData = response . data ;
44
+ setProject ( projectData ) ;
45
+ setStarCount ( projectData . starCount ) ; // Set star count when project data is fetched
37
46
} else {
38
47
console . error ( "Failed to fetch project details" ) ;
39
48
}
@@ -47,6 +56,41 @@ const ProjectDisplay = () => {
47
56
fetchProject ( ) ;
48
57
} , [ username , projectId ] ) ;
49
58
59
+ useEffect ( ( ) => {
60
+ if ( project ?. projectId ) {
61
+ const starredStatus = localStorage . getItem ( `starred_${ project . projectId } ` ) ;
62
+ setIsStarred ( ! ! starredStatus ) ;
63
+ }
64
+ } , [ project ?. projectId ] ) ;
65
+
66
+ const handleStarClick = async ( ) => {
67
+ if ( isStarred || isStarRequestInProgress || ! project ) return ; // Prevent multiple stars or if request is in progress
68
+
69
+ setIsStarRequestInProgress ( true ) ; // Disable the button while the request is in progress
70
+
71
+ try {
72
+ const response = await fetch ( `${ backendUrl } /profile/${ username } /projects/${ project . projectId } /star` , {
73
+ method : "POST" ,
74
+ headers : { "Content-Type" : "application/json" } ,
75
+ } ) ;
76
+
77
+ if ( response . ok ) {
78
+ setStarCount ( ( prevCount ) => prevCount + 1 ) ;
79
+ setIsStarred ( true ) ;
80
+ localStorage . setItem ( `starred_${ project . projectId } ` , "true" ) ;
81
+ toast . success ( "Project starred!" ) ;
82
+ } else {
83
+ console . error ( "Failed to star the project" ) ;
84
+ toast . error ( "Failed to star the project" ) ;
85
+ }
86
+ } catch ( error ) {
87
+ console . error ( "Error starring the project:" , error ) ;
88
+ toast . error ( "Error starring the project" ) ;
89
+ } finally {
90
+ setIsStarRequestInProgress ( false ) ; // Re-enable the button after request finishes
91
+ }
92
+ } ;
93
+
50
94
return (
51
95
< SidebarProvider >
52
96
< SidebarLeft />
@@ -64,30 +108,58 @@ const ProjectDisplay = () => {
64
108
< div className = "space-y-4" >
65
109
{ project . imageUrl && (
66
110
< div className = "md:h-[440px] sm:h-auto overflow-hidden" >
67
- < img src = { project . imageUrl } alt = { project . title } className = "w-full h-full object-cover" />
111
+ < img
112
+ src = { project . imageUrl }
113
+ alt = { project . title }
114
+ className = "w-full h-full object-cover"
115
+ />
68
116
</ div >
69
117
) }
70
118
< div className = "w-full p-6" >
71
- < div className = ' flex' >
119
+ < div className = " flex items-center" >
72
120
< h1 className = "text-4xl font-bold" > { project . title } </ h1 >
73
- < div className = 'flex-grow' > </ div >
74
- < div className = "p-2 flex items-center" >
75
- < p className = "text-lg text-gray-500" > Stars: { project . starCount } </ p >
121
+ < div className = "flex-grow" > </ div >
122
+ < div className = "p-2 flex items-center" >
123
+ < Button
124
+ variant = "secondary"
125
+ className = "shadow-none"
126
+ onClick = { handleStarClick }
127
+ disabled = { isStarred || isStarRequestInProgress } // Disable during request
128
+ >
129
+ { isStarred ? (
130
+ < FilledStarIcon className = "mr-2 h-4 w-4 text-yellow-400" />
131
+ ) : (
132
+ < StarIcon className = "mr-2 h-4 w-4" />
133
+ ) }
134
+ { starCount }
135
+ </ Button >
76
136
</ div >
77
137
</ div >
78
138
< div className = "flex items-center mt-2" >
79
139
< h2 className = "text-lg font-medium mr-2" > Tags:</ h2 >
80
140
< div className = "flex gap-2" >
81
141
{ project . tags . map ( ( tag , index ) => (
82
- < span key = { index } className = "px-2 py-1 dark:bg-gray-200 rounded-full text-sm dark:text-black" >
142
+ < span
143
+ key = { index }
144
+ className = "px-2 py-1 dark:bg-gray-200 rounded-full text-sm dark:text-black"
145
+ >
83
146
{ tag }
84
147
</ span >
85
148
) ) }
86
149
</ div >
87
150
</ div >
88
- < p className = "text-lg text-gray-500 mt-2" > Repo: < a href = { project . repoLink } target = "_blank" rel = "noopener noreferrer" className = "text-blue-600" > { project . repoLink } </ a > </ p >
151
+ < p className = "text-lg text-gray-500 mt-2" >
152
+ < a
153
+ href = { project . repoLink }
154
+ target = "_blank"
155
+ rel = "noopener noreferrer"
156
+ className = "text-blue-600"
157
+ >
158
+ Github Repository
159
+ </ a >
160
+ </ p >
89
161
< h1 className = "text-4xl font-bold mt-5" > Project Description:</ h1 >
90
- < ReactMarkdown className = "mt-4" > { project . description } </ ReactMarkdown >
162
+ < ReactMarkdown className = "mt-4" > { project . description } </ ReactMarkdown >
91
163
</ div >
92
164
</ div >
93
165
) : (
0 commit comments