@@ -4,9 +4,11 @@ import {
4
4
Github ,
5
5
Globe ,
6
6
Heart ,
7
+ Star ,
7
8
Terminal ,
8
9
} from "lucide-react" ;
9
10
import Image from "next/image" ;
11
+ // import Link from "next/link";
10
12
import { useEffect , useState } from "react" ;
11
13
import type { Sponsor } from "@/lib/types" ;
12
14
@@ -48,13 +50,22 @@ export default function SponsorsSection() {
48
50
) ;
49
51
}
50
52
53
+ const aAmount = getAmount ( a ) ;
54
+ const bAmount = getAmount ( b ) ;
55
+
56
+ if ( aAmount !== bAmount ) {
57
+ return bAmount - aAmount ;
58
+ }
59
+
51
60
const aIsMonthly = ! a . isOneTime ;
52
61
const bIsMonthly = ! b . isOneTime ;
53
62
54
63
if ( aIsMonthly && ! bIsMonthly ) return - 1 ;
55
64
if ( ! aIsMonthly && bIsMonthly ) return 1 ;
56
65
57
- return getAmount ( b ) - getAmount ( a ) ;
66
+ return (
67
+ new Date ( a . createdAt ) . getTime ( ) - new Date ( b . createdAt ) . getTime ( )
68
+ ) ;
58
69
} ) ;
59
70
60
71
setSponsors ( sortedSponsors ) ;
@@ -66,13 +77,46 @@ export default function SponsorsSection() {
66
77
} ) ;
67
78
} , [ ] ) ;
68
79
80
+ const SPECIAL_SPONSOR_THRESHOLD = 100 ;
81
+
82
+ const getSponsorAmount = ( sponsor : Sponsor ) => {
83
+ if ( sponsor . monthlyDollars === - 1 ) {
84
+ if ( sponsor . tierName ) {
85
+ const match = sponsor . tierName . match ( / \$ ( \d + (?: \. \d + ) ? ) / ) ;
86
+ return match ? Number . parseFloat ( match [ 1 ] ) : 0 ;
87
+ }
88
+ return 0 ;
89
+ }
90
+
91
+ if ( ! sponsor . isOneTime && sponsor . monthlyDollars > 0 ) {
92
+ return sponsor . monthlyDollars ;
93
+ }
94
+
95
+ if ( sponsor . isOneTime && sponsor . tierName ) {
96
+ const match = sponsor . tierName . match ( / \$ ( \d + (?: \. \d + ) ? ) / ) ;
97
+ return match ? Number . parseFloat ( match [ 1 ] ) : 0 ;
98
+ }
99
+
100
+ return 0 ;
101
+ } ;
102
+
103
+ const isSpecialSponsor = ( sponsor : Sponsor ) => {
104
+ const amount = getSponsorAmount ( sponsor ) ;
105
+ return amount >= SPECIAL_SPONSOR_THRESHOLD ;
106
+ } ;
107
+
69
108
const currentSponsors = sponsors . filter (
70
109
( sponsor ) => sponsor . monthlyDollars !== - 1 ,
71
110
) ;
72
111
const pastSponsors = sponsors . filter (
73
112
( sponsor ) => sponsor . monthlyDollars === - 1 ,
74
113
) ;
75
114
115
+ const specialSponsors = currentSponsors . filter ( isSpecialSponsor ) ;
116
+ const regularSponsors = currentSponsors . filter (
117
+ ( sponsor ) => ! isSpecialSponsor ( sponsor ) ,
118
+ ) ;
119
+
76
120
return (
77
121
< div className = "mb-12" >
78
122
< div className = "mb-6 flex flex-wrap items-center justify-between gap-2 sm:flex-nowrap" >
@@ -133,23 +177,19 @@ export default function SponsorsSection() {
133
177
</ div >
134
178
) : (
135
179
< div className = "space-y-8" >
136
- { currentSponsors . length > 0 && (
180
+ { specialSponsors . length > 0 && (
137
181
< div className = "space-y-4" >
138
- { /* <div className="flex items-center gap-2">
139
- <span className="text-primary text-sm">▶</span>
140
- <span className="font-semibold text-foreground text-sm">
141
- ACTIVE_SPONSORS.SH
142
- </span>
143
- <span className="text-muted-foreground text-xs">
144
- ({currentSponsors.length})
145
- </span>
146
- </div> */ }
147
182
< div className = "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4" >
148
- { currentSponsors . map ( ( entry , index ) => {
183
+ { specialSponsors . map ( ( entry , index ) => {
149
184
const since = new Date ( entry . createdAt ) . toLocaleDateString (
150
185
undefined ,
151
186
{ year : "numeric" , month : "short" } ,
152
187
) ;
188
+ const sponsorUrl =
189
+ entry . sponsor . websiteUrl ||
190
+ entry . sponsor . linkUrl ||
191
+ `https://github.com/${ entry . sponsor . login } ` ;
192
+
153
193
return (
154
194
< div
155
195
key = { entry . sponsor . login }
@@ -158,18 +198,102 @@ export default function SponsorsSection() {
158
198
>
159
199
< div className = "border-border border-b px-3 py-2" >
160
200
< div className = "flex items-center gap-2" >
161
- < span className = "text-primary text-xs" > ▶ </ span >
201
+ < Star className = "h-4 w-4 text-yellow-500/90" / >
162
202
< div className = "ml-auto flex items-center gap-2 text-muted-foreground text-xs" >
163
- < span >
164
- { entry . isOneTime ? "ONE-TIME" : "MONTHLY" }
165
- </ span >
203
+ < span > SPECIAL</ span >
166
204
< span > •</ span >
167
205
< span > SINCE { since . toUpperCase ( ) } </ span >
168
206
</ div >
169
207
</ div >
170
208
</ div >
171
209
< div className = "p-4" >
172
- < div className = "flex items-center gap-4" >
210
+ < div className = "flex gap-4" >
211
+ < div className = "flex-shrink-0" >
212
+ < Image
213
+ src = {
214
+ entry . sponsor . customLogoUrl ||
215
+ entry . sponsor . avatarUrl
216
+ }
217
+ alt = { entry . sponsor . name || entry . sponsor . login }
218
+ width = { 100 }
219
+ height = { 100 }
220
+ className = "rounded border border-border transition-colors duration-300"
221
+ unoptimized
222
+ />
223
+ </ div >
224
+ < div className = "grid grid-cols-1 grid-rows-[1fr_auto] justify-between py-2" >
225
+ < div >
226
+ < h3 className = "truncate font-semibold text-foreground text-sm" >
227
+ { entry . sponsor . name || entry . sponsor . login }
228
+ </ h3 >
229
+ { entry . tierName && (
230
+ < p className = " text-primary text-xs" >
231
+ { entry . tierName }
232
+ </ p >
233
+ ) }
234
+ </ div >
235
+ < div className = "flex flex-col gap-1" >
236
+ < a
237
+ href = { `https://github.com/${ entry . sponsor . login } ` }
238
+ target = "_blank"
239
+ rel = "noopener noreferrer"
240
+ className = "group flex items-center gap-2 text-muted-foreground text-xs transition-colors hover:text-primary"
241
+ >
242
+ < Github className = "h-4 w-4" />
243
+ < span className = "truncate" >
244
+ { entry . sponsor . login }
245
+ </ span >
246
+ </ a >
247
+ { ( entry . sponsor . websiteUrl ||
248
+ entry . sponsor . linkUrl ) && (
249
+ < a
250
+ href = { sponsorUrl }
251
+ target = "_blank"
252
+ rel = "noopener noreferrer"
253
+ className = "group flex items-center gap-2 text-muted-foreground text-xs transition-colors hover:text-primary"
254
+ >
255
+ < Globe className = "h-4 w-4" />
256
+ < span className = "truncate" >
257
+ { sponsorUrl
258
+ ?. replace ( / ^ h t t p s ? : \/ \/ / , "" )
259
+ ?. replace ( / \/ $ / , "" ) }
260
+ </ span >
261
+ </ a >
262
+ ) }
263
+ </ div >
264
+ </ div >
265
+ </ div >
266
+ </ div >
267
+ </ div >
268
+ ) ;
269
+ } ) }
270
+ </ div >
271
+ </ div >
272
+ ) }
273
+ { regularSponsors . length > 0 && (
274
+ < div className = "space-y-4" >
275
+ < div className = "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4" >
276
+ { regularSponsors . map ( ( entry , index ) => {
277
+ const since = new Date ( entry . createdAt ) . toLocaleDateString (
278
+ undefined ,
279
+ { year : "numeric" , month : "short" } ,
280
+ ) ;
281
+ return (
282
+ < div
283
+ key = { entry . sponsor . login }
284
+ className = "rounded border border-border"
285
+ style = { { animationDelay : `${ index * 50 } ms` } }
286
+ >
287
+ < div className = "border-border border-b px-3 py-2" >
288
+ < div className = "flex items-center gap-2" >
289
+ < span className = "text-primary text-xs" > ▶</ span >
290
+ < div className = "ml-auto flex items-center gap-2 text-muted-foreground text-xs" >
291
+ < span > SINCE { since . toUpperCase ( ) } </ span >
292
+ </ div >
293
+ </ div >
294
+ </ div >
295
+ < div className = "p-4" >
296
+ < div className = "flex gap-4" >
173
297
< div className = "flex-shrink-0" >
174
298
< Image
175
299
src = { entry . sponsor . avatarUrl }
@@ -180,7 +304,7 @@ export default function SponsorsSection() {
180
304
unoptimized
181
305
/>
182
306
</ div >
183
- < div className = "min-w-0 flex-1 space-y -2" >
307
+ < div className = "grid grid-cols-1 grid-rows-[1fr_auto] justify-between py -2" >
184
308
< div >
185
309
< h3 className = "truncate font-semibold text-foreground text-sm" >
186
310
{ entry . sponsor . name || entry . sponsor . login }
@@ -267,6 +391,12 @@ export default function SponsorsSection() {
267
391
undefined ,
268
392
{ year : "numeric" , month : "short" } ,
269
393
) ;
394
+ const wasSpecial = isSpecialSponsor ( entry ) ;
395
+ const sponsorUrl =
396
+ entry . sponsor . websiteUrl ||
397
+ entry . sponsor . linkUrl ||
398
+ `https://github.com/${ entry . sponsor . login } ` ;
399
+
270
400
return (
271
401
< div
272
402
key = { entry . sponsor . login }
@@ -275,29 +405,36 @@ export default function SponsorsSection() {
275
405
>
276
406
< div className = "border-border/70 border-b px-3 py-2" >
277
407
< div className = "flex items-center gap-2" >
278
- < span className = "text-muted-foreground text-xs" >
279
- ◆
280
- </ span >
408
+ { wasSpecial ? (
409
+ < Star className = "h-4 w-4 text-yellow-500/60" />
410
+ ) : (
411
+ < span className = "text-muted-foreground text-xs" >
412
+ ◆
413
+ </ span >
414
+ ) }
281
415
< div className = "ml-auto flex items-center gap-2 text-muted-foreground text-xs" >
282
- < span > PAST </ span >
283
- < span > •</ span >
416
+ { wasSpecial && < span > SPECIAL </ span > }
417
+ { wasSpecial && < span > •</ span > }
284
418
< span > SINCE { since . toUpperCase ( ) } </ span >
285
419
</ div >
286
420
</ div >
287
421
</ div >
288
422
< div className = "p-4" >
289
- < div className = "flex items-center gap-4" >
423
+ < div className = "flex gap-4" >
290
424
< div className = "flex-shrink-0" >
291
425
< Image
292
- src = { entry . sponsor . avatarUrl }
426
+ src = {
427
+ entry . sponsor . customLogoUrl ||
428
+ entry . sponsor . avatarUrl
429
+ }
293
430
alt = { entry . sponsor . name || entry . sponsor . login }
294
431
width = { 80 }
295
432
height = { 80 }
296
- className = "rounded border border-border/70 transition-colors duration-300 "
433
+ className = "rounded border border-border/70"
297
434
unoptimized
298
435
/>
299
436
</ div >
300
- < div className = "min-w-0 flex-1 space-y-2 " >
437
+ < div className = "grid grid-cols-1 grid-rows-[1fr_auto] justify-between " >
301
438
< div >
302
439
< h3 className = "truncate font-semibold text-muted-foreground text-sm" >
303
440
{ entry . sponsor . name || entry . sponsor . login }
@@ -323,20 +460,14 @@ export default function SponsorsSection() {
323
460
{ ( entry . sponsor . websiteUrl ||
324
461
entry . sponsor . linkUrl ) && (
325
462
< a
326
- href = {
327
- entry . sponsor . websiteUrl ||
328
- entry . sponsor . linkUrl
329
- }
463
+ href = { sponsorUrl }
330
464
target = "_blank"
331
465
rel = "noopener noreferrer"
332
466
className = "group flex items-center gap-2 text-muted-foreground/70 text-xs transition-colors hover:text-muted-foreground"
333
467
>
334
468
< Globe className = "h-4 w-4" />
335
469
< span className = "truncate" >
336
- { (
337
- entry . sponsor . websiteUrl ||
338
- entry . sponsor . linkUrl
339
- )
470
+ { sponsorUrl
340
471
?. replace ( / ^ h t t p s ? : \/ \/ / , "" )
341
472
?. replace ( / \/ $ / , "" ) }
342
473
</ span >
0 commit comments