@@ -32,6 +32,7 @@ import {
3232 CardTitle ,
3333} from "~/components/ui/card" ;
3434import { Input } from "~/components/ui/input" ;
35+ import { Progress } from "~/components/ui/progress" ;
3536import { Skeleton } from "~/components/ui/skeleton" ;
3637import { cn , formatNumber } from "~/lib/utils" ;
3738import { api } from "~/trpc/react" ;
@@ -44,25 +45,59 @@ function getDeveloperTier(downloads: number, rating: number, plugins: number) {
4445 name : "Legend" ,
4546 color : "from-yellow-400 to-orange-500" ,
4647 icon : Crown ,
48+ bgColor : "from-yellow-50 to-orange-50 dark:from-yellow-900/20 dark:to-orange-900/20" ,
4749 } ;
4850 if ( score >= 5000 )
4951 return {
5052 name : "Master" ,
5153 color : "from-purple-400 to-pink-500" ,
5254 icon : Trophy ,
55+ bgColor : "from-purple-50 to-pink-50 dark:from-purple-900/20 dark:to-pink-900/20" ,
5356 } ;
5457 if ( score >= 2000 )
55- return { name : "Expert" , color : "from-blue-400 to-cyan-500" , icon : Award } ;
58+ return {
59+ name : "Expert" ,
60+ color : "from-blue-400 to-cyan-500" ,
61+ icon : Award ,
62+ bgColor : "from-blue-50 to-cyan-50 dark:from-blue-900/20 dark:to-cyan-900/20" ,
63+ } ;
5664 if ( score >= 500 )
5765 return {
5866 name : "Pro" ,
5967 color : "from-green-400 to-emerald-500" ,
6068 icon : Target ,
69+ bgColor : "from-green-50 to-emerald-50 dark:from-green-900/20 dark:to-emerald-900/20" ,
6170 } ;
6271 return {
6372 name : "Rising" ,
6473 color : "from-gray-400 to-slate-500" ,
6574 icon : Sparkles ,
75+ bgColor : "from-gray-50 to-slate-50 dark:from-gray-900/20 dark:to-slate-900/20" ,
76+ } ;
77+ }
78+
79+ function getTierProgress ( downloads : number , rating : number , plugins : number ) {
80+ const score = downloads * 0.6 + rating * plugins * 20 ;
81+ const tiers = [ 0 , 500 , 2000 , 5000 , 10000 ] ;
82+ const tierNames = [ "Rising" , "Pro" , "Expert" , "Master" , "Legend" ] ;
83+
84+ let currentTier = 0 ;
85+ for ( let i = 0 ; i < tiers . length ; i ++ ) {
86+ if ( score >= tiers [ i ] ! ) currentTier = i ;
87+ }
88+
89+ if ( currentTier === tiers . length - 1 ) return { progress : 100 , nextTier : null } ;
90+
91+ const currentTierScore = tiers [ currentTier ] ! ;
92+ const nextTierScore = tiers [ currentTier + 1 ] ! ;
93+ const current = score - currentTierScore ;
94+ const next = nextTierScore - currentTierScore ;
95+ const progress = Math . min ( ( current / next ) * 100 , 100 ) ;
96+
97+ return {
98+ progress,
99+ nextTier : tierNames [ currentTier + 1 ] || null ,
100+ scoreNeeded : nextTierScore - score
66101 } ;
67102}
68103
@@ -162,87 +197,108 @@ export default function DevelopersPage() {
162197 ) : (
163198 < >
164199 < div className = "grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4" >
165- { filteredDevelopers . map ( ( developer : any ) => {
200+ { filteredDevelopers . map ( ( developer : any ) => {
166201 const tier = getDeveloperTier (
167202 developer . totalDownloads || 0 ,
168203 developer . averageRating || 0 ,
169204 developer . pluginCount || 0 ,
170205 ) ;
206+ const progress = getTierProgress (
207+ developer . totalDownloads || 0 ,
208+ developer . averageRating || 0 ,
209+ developer . pluginCount || 0 ,
210+ ) ;
171211 const TierIcon = tier . icon ;
172212
173213 return (
174214 < Card
175215 key = { developer . id }
176- className = "group hover:-translate-y-1 h-full cursor-pointer overflow-hidden transition-all duration-200 hover:shadow-lg"
216+ className = "group relative h-full cursor-pointer overflow-hidden bg-gradient-to-br from-card to-card/50 transition-all duration-300 hover:-translate-y-1 hover:shadow-xl"
217+ onClick = { ( ) =>
218+ ( window . location . href = `/developers/${ developer . id } ` )
219+ }
177220 >
178- < CardContent
179- className = "p-6"
180- onClick = { ( ) =>
181- ( window . location . href = `/developers/${ developer . id } ` )
182- }
183- >
184- < div className = "flex items-start gap-4" >
221+ { /* Tier accent border */ }
222+ < div className = { cn (
223+ "absolute inset-x-0 top-0 h-1 bg-gradient-to-r" ,
224+ tier . color
225+ ) } />
226+
227+ < CardContent className = "relative p-6 h-full flex flex-col" >
228+ { /* Header with avatar and tier */ }
229+ < div className = "flex items-start gap-4 mb-4" >
185230 < div className = "relative" >
186- < Avatar className = "h-12 w-12 " >
231+ < Avatar className = "h-14 w-14 border-2 border-background shadow-lg " >
187232 < AvatarImage
188233 src = { developer . image || undefined }
189234 alt = { developer . name || "" }
190235 className = "object-cover"
191236 />
192- < AvatarFallback className = "bg-primary/10 font-medium text-sm" >
237+ < AvatarFallback className = { cn (
238+ "bg-gradient-to-br font-medium text-sm text-white" ,
239+ tier . color
240+ ) } >
193241 { ( developer . name || "??" )
194242 . slice ( 0 , 2 )
195243 . toUpperCase ( ) }
196244 </ AvatarFallback >
197245 </ Avatar >
198246 < div
199247 className = { cn (
200- "-bottom-1 -right-1 absolute flex h-6 w-6 items-center justify-center rounded-full bg-gradient-to-r shadow-sm " ,
248+ "-bottom-1 -right-1 absolute flex h-7 w-7 items-center justify-center rounded-full bg-gradient-to-r shadow-lg border-2 border-background " ,
201249 tier . color ,
202250 ) }
203251 >
204- < TierIcon className = "h-3 w-3 text-white" />
252+ < TierIcon className = "h-3.5 w-3.5 text-white" />
205253 </ div >
206254 </ div >
207255
208256 < div className = "min-w-0 flex-1" >
209- < h3 className = "truncate font-semibold transition-colors group-hover:text-primary" >
257+ < h3 className = "truncate font-bold text-lg transition-colors group-hover:text-primary" >
210258 { developer . name || "Anonymous" }
211259 </ h3 >
212- < div className = "mt-1 flex items-center gap- 2" >
260+ < div className = "mt-2" >
213261 < Badge
214- variant = "secondary"
215262 className = { cn (
216- "border-0 bg-gradient-to-r text-white text-xs" ,
263+ "border-0 bg-gradient-to-r text-white text-xs shadow-md px-3 py-1 " ,
217264 tier . color ,
218265 ) }
219266 >
220- < TierIcon className = "mr-1 h-2.5 w-2.5 " />
221- { tier . name }
267+ < TierIcon className = "mr-1.5 h-3 w-3 " />
268+ { tier . name } Developer
222269 </ Badge >
223- { developer . isVerified && (
224- < Badge
225- variant = "outline"
226- className = "border-blue-500 text-blue-600 text-xs"
227- >
228- < Zap className = "mr-1 h-2.5 w-2.5" />
229- Verified
230- </ Badge >
231- ) }
232270 </ div >
233271 </ div >
234272 </ div >
235273
236274 { developer . bio && (
237- < p className = "mt -4 line-clamp-2 text-muted-foreground text-sm" >
275+ < p className = "mb -4 line-clamp-2 text-muted-foreground text-sm" >
238276 { developer . bio }
239277 </ p >
240278 ) }
241279
242- < div className = "mt-4 grid grid-cols-3 gap-2 text-center" >
243- < div className = "rounded-lg bg-muted/50 p-2" >
280+ { /* Progress to next tier */ }
281+ { progress . nextTier && progress . progress < 100 && (
282+ < div className = "mb-4" >
283+ < div className = "mb-2 flex items-center justify-between text-xs" >
284+ < span className = "text-muted-foreground" >
285+ Прогресс до { progress . nextTier }
286+ </ span >
287+ < span className = "font-medium text-primary" >
288+ { Math . round ( progress . progress ) } %
289+ </ span >
290+ </ div >
291+ < Progress
292+ value = { progress . progress }
293+ className = "h-1.5"
294+ />
295+ </ div >
296+ ) }
297+
298+ < div className = "mb-4 grid grid-cols-3 gap-2 text-center" >
299+ < div className = "rounded-lg bg-muted/30 p-2 transition-colors group-hover:bg-muted/50" >
244300 < div className = "flex items-center justify-center gap-1" >
245- < Package className = "h-3 w-3 text-muted-foreground " />
301+ < Package className = "h-3 w-3 text-blue-600 " />
246302 < span className = "font-medium text-sm" >
247303 { developer . pluginCount || 0 }
248304 </ span >
@@ -252,9 +308,9 @@ export default function DevelopersPage() {
252308 </ div >
253309 </ div >
254310
255- < div className = "rounded-lg bg-muted/50 p-2" >
311+ < div className = "rounded-lg bg-muted/30 p-2 transition-colors group-hover:bg-muted/50 " >
256312 < div className = "flex items-center justify-center gap-1" >
257- < Download className = "h-3 w-3 text-muted-foreground " />
313+ < Download className = "h-3 w-3 text-green-600 " />
258314 < span className = "font-medium text-sm" >
259315 { formatNumber ( developer . totalDownloads || 0 ) }
260316 </ span >
@@ -264,9 +320,9 @@ export default function DevelopersPage() {
264320 </ div >
265321 </ div >
266322
267- < div className = "rounded-lg bg-muted/50 p-2" >
323+ < div className = "rounded-lg bg-muted/30 p-2 transition-colors group-hover:bg-muted/50 " >
268324 < div className = "flex items-center justify-center gap-1" >
269- < Star className = "h-3 w-3 text-muted-foreground " />
325+ < Star className = "h-3 w-3 text-yellow-600 " />
270326 < span className = "font-medium text-sm" >
271327 { developer . averageRating ?. toFixed ( 1 ) || "0.0" }
272328 </ span >
@@ -277,13 +333,13 @@ export default function DevelopersPage() {
277333 </ div >
278334 </ div >
279335
280- < div className = "mt-4 flex items-center justify-between" >
336+ < div className = "mt-auto flex items-center justify-between" >
281337 < div className = "flex items-center gap-1" >
282338 { developer . githubUsername && (
283339 < Button
284340 variant = "ghost"
285341 size = "sm"
286- className = "h-8 w-8 p-0"
342+ className = "h-8 w-8 p-0 hover:bg-primary/10 transition-colors "
287343 onClick = { ( e ) => {
288344 e . stopPropagation ( ) ;
289345 window . open (
@@ -299,7 +355,7 @@ export default function DevelopersPage() {
299355 < Button
300356 variant = "ghost"
301357 size = "sm"
302- className = "h-8 w-8 p-0"
358+ className = "h-8 w-8 p-0 hover:bg-primary/10 transition-colors "
303359 onClick = { ( e ) => {
304360 e . stopPropagation ( ) ;
305361 window . open ( developer . website , "_blank" ) ;
@@ -313,7 +369,7 @@ export default function DevelopersPage() {
313369 < Button
314370 size = "sm"
315371 variant = "ghost"
316- className = "h-8 w-8 p-0"
372+ className = "h-8 w-8 p-0 hover:bg-primary/10 transition-colors opacity-60 group-hover:opacity-100 "
317373 >
318374 < ExternalLink className = "h-4 w-4" />
319375 </ Button >
0 commit comments