Skip to content

Commit 909263a

Browse files
fix sponsor data handling
1 parent 12cbbe5 commit 909263a

File tree

5 files changed

+145
-131
lines changed

5 files changed

+145
-131
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations
44

5-
![demo](https://cdn.jsdelivr.net/gh/amanvarshney01/create-better-t-stack/demo.gif)
6-
75
## Sponsors
86

97
<p align="center">
108
<img src="https://sponsors.amanv.dev/sponsors.png" alt="Sponsors">
119
</p>
1210

11+
![demo](https://cdn.jsdelivr.net/gh/amanvarshney01/create-better-t-stack/demo.gif)
12+
1313
## Quick Start
1414

1515
```bash

apps/cli/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations
44

5-
![demo](https://cdn.jsdelivr.net/gh/amanvarshney01/create-better-t-stack@master/demo.gif)
6-
75
## Sponsors
86

97
<p align="center">
108
<img src="https://sponsors.amanv.dev/sponsors.png" alt="Sponsors">
119
</p>
1210

11+
![demo](https://cdn.jsdelivr.net/gh/amanvarshney01/create-better-t-stack@master/demo.gif)
12+
1313
## Quick Start
1414

1515
Run without installing globally:

apps/web/src/app/(home)/_components/sponsors-section.tsx

Lines changed: 24 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ import {
1010
import Image from "next/image";
1111
// import Link from "next/link";
1212
import { useEffect, useState } from "react";
13+
import {
14+
filterCurrentSponsors,
15+
filterPastSponsors,
16+
filterRegularSponsors,
17+
filterSpecialSponsors,
18+
formatSponsorUrl,
19+
getSponsorUrl,
20+
isSpecialSponsor,
21+
sortSpecialSponsors,
22+
sortSponsors,
23+
} from "@/lib/sponsor-utils";
1324
import type { Sponsor } from "@/lib/types";
1425

1526
export default function SponsorsSection() {
@@ -26,48 +37,7 @@ export default function SponsorsSection() {
2637
})
2738
.then((data) => {
2839
const sponsorsData = Array.isArray(data) ? data : [];
29-
30-
const sortedSponsors = sponsorsData.sort((a, b) => {
31-
const getAmount = (sponsor: Sponsor) => {
32-
if (!sponsor.isOneTime && sponsor.monthlyDollars > 0) {
33-
return sponsor.monthlyDollars;
34-
}
35-
36-
if (sponsor.tierName) {
37-
const match = sponsor.tierName.match(/\$(\d+(?:\.\d+)?)/);
38-
return match ? Number.parseFloat(match[1]) : 0;
39-
}
40-
41-
return 0;
42-
};
43-
44-
if (a.monthlyDollars === -1 && b.monthlyDollars !== -1) return 1;
45-
if (a.monthlyDollars !== -1 && b.monthlyDollars === -1) return -1;
46-
47-
if (a.monthlyDollars === -1 && b.monthlyDollars === -1) {
48-
return (
49-
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
50-
);
51-
}
52-
53-
const aAmount = getAmount(a);
54-
const bAmount = getAmount(b);
55-
56-
if (aAmount !== bAmount) {
57-
return bAmount - aAmount;
58-
}
59-
60-
const aIsMonthly = !a.isOneTime;
61-
const bIsMonthly = !b.isOneTime;
62-
63-
if (aIsMonthly && !bIsMonthly) return -1;
64-
if (!aIsMonthly && bIsMonthly) return 1;
65-
66-
return (
67-
new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
68-
);
69-
});
70-
40+
const sortedSponsors = sortSponsors(sponsorsData);
7141
setSponsors(sortedSponsors);
7242
setLoadingSponsors(false);
7343
})
@@ -77,45 +47,13 @@ export default function SponsorsSection() {
7747
});
7848
}, []);
7949

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-
108-
const currentSponsors = sponsors.filter(
109-
(sponsor) => sponsor.monthlyDollars !== -1,
110-
);
111-
const pastSponsors = sponsors.filter(
112-
(sponsor) => sponsor.monthlyDollars === -1,
113-
);
50+
const currentSponsors = filterCurrentSponsors(sponsors);
51+
const pastSponsors = filterPastSponsors(sponsors);
11452

115-
const specialSponsors = currentSponsors.filter(isSpecialSponsor);
116-
const regularSponsors = currentSponsors.filter(
117-
(sponsor) => !isSpecialSponsor(sponsor),
53+
const specialSponsors = sortSpecialSponsors(
54+
filterSpecialSponsors(currentSponsors),
11855
);
56+
const regularSponsors = filterRegularSponsors(currentSponsors);
11957

12058
return (
12159
<div className="mb-12">
@@ -185,10 +123,7 @@ export default function SponsorsSection() {
185123
undefined,
186124
{ year: "numeric", month: "short" },
187125
);
188-
const sponsorUrl =
189-
entry.sponsor.websiteUrl ||
190-
entry.sponsor.linkUrl ||
191-
`https://github.com/${entry.sponsor.login}`;
126+
const sponsorUrl = getSponsorUrl(entry);
192127

193128
return (
194129
<div
@@ -254,9 +189,7 @@ export default function SponsorsSection() {
254189
>
255190
<Globe className="h-4 w-4" />
256191
<span className="truncate">
257-
{sponsorUrl
258-
?.replace(/^https?:\/\//, "")
259-
?.replace(/\/$/, "")}
192+
{formatSponsorUrl(sponsorUrl)}
260193
</span>
261194
</a>
262195
)}
@@ -340,12 +273,10 @@ export default function SponsorsSection() {
340273
>
341274
<Globe className="h-4 w-4" />
342275
<span className="truncate">
343-
{(
276+
{formatSponsorUrl(
344277
entry.sponsor.websiteUrl ||
345-
entry.sponsor.linkUrl
346-
)
347-
?.replace(/^https?:\/\//, "")
348-
?.replace(/\/$/, "")}
278+
entry.sponsor.linkUrl,
279+
)}
349280
</span>
350281
</a>
351282
)}
@@ -392,10 +323,7 @@ export default function SponsorsSection() {
392323
{ year: "numeric", month: "short" },
393324
);
394325
const wasSpecial = isSpecialSponsor(entry);
395-
const sponsorUrl =
396-
entry.sponsor.websiteUrl ||
397-
entry.sponsor.linkUrl ||
398-
`https://github.com/${entry.sponsor.login}`;
326+
const sponsorUrl = getSponsorUrl(entry);
399327

400328
return (
401329
<div
@@ -467,9 +395,7 @@ export default function SponsorsSection() {
467395
>
468396
<Globe className="h-4 w-4" />
469397
<span className="truncate">
470-
{sponsorUrl
471-
?.replace(/^https?:\/\//, "")
472-
?.replace(/\/$/, "")}
398+
{formatSponsorUrl(sponsorUrl)}
473399
</span>
474400
</a>
475401
)}

apps/web/src/components/special-sponsor-banner.tsx

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
"use client";
22
import Image from "next/image";
33
import { useEffect, useState } from "react";
4+
import {
5+
filterCurrentSponsors,
6+
filterSpecialSponsors,
7+
sortSpecialSponsors,
8+
} from "@/lib/sponsor-utils";
49
import type { Sponsor } from "@/lib/types";
510

6-
const isSpecialSponsor = (sponsor: Sponsor) => {
7-
return sponsor.monthlyDollars >= 100;
8-
};
9-
1011
export function SpecialSponsorBanner() {
11-
const [specialSponsor, setSpecialSponsor] = useState<Sponsor | null>(null);
12+
const [specialSponsors, setSpecialSponsors] = useState<Sponsor[]>([]);
1213
const [loading, setLoading] = useState(true);
1314

1415
useEffect(() => {
@@ -19,13 +20,11 @@ export function SpecialSponsorBanner() {
1920
})
2021
.then((data) => {
2122
const sponsorsData = Array.isArray(data) ? data : [];
22-
const specialSponsor = sponsorsData.find(
23-
(sponsor) => sponsor.monthlyDollars > 0 && isSpecialSponsor(sponsor),
23+
const currentSponsors = filterCurrentSponsors(sponsorsData);
24+
const specials = sortSpecialSponsors(
25+
filterSpecialSponsors(currentSponsors),
2426
);
25-
26-
if (specialSponsor) {
27-
setSpecialSponsor(specialSponsor);
28-
}
27+
setSpecialSponsors(specials);
2928
setLoading(false);
3029
})
3130
.catch(() => {
@@ -46,29 +45,30 @@ export function SpecialSponsorBanner() {
4645
);
4746
}
4847

49-
if (!specialSponsor) {
48+
if (!specialSponsors.length) {
5049
return null;
5150
}
5251

5352
return (
5453
<div className="">
55-
<div className="flex items-center gap-3">
56-
<Image
57-
src={
58-
specialSponsor.sponsor.customLogoUrl ||
59-
specialSponsor.sponsor.avatarUrl
60-
}
61-
alt={specialSponsor.sponsor.name || specialSponsor.sponsor.login}
62-
width={30}
63-
height={30}
64-
className="rounded border border-border"
65-
unoptimized
66-
/>
67-
<div className="min-w-0 flex-1">
68-
<h4 className="truncate font-medium text-sm">
69-
{specialSponsor.sponsor.name || specialSponsor.sponsor.login}
70-
</h4>
71-
</div>
54+
<div className="flex flex-col gap-2">
55+
{specialSponsors.map((sponsor) => (
56+
<div key={sponsor.sponsor.login} className="flex items-center gap-3">
57+
<Image
58+
src={sponsor.sponsor.customLogoUrl || sponsor.sponsor.avatarUrl}
59+
alt={sponsor.sponsor.name || sponsor.sponsor.login}
60+
width={30}
61+
height={30}
62+
className="rounded border border-border"
63+
unoptimized
64+
/>
65+
<div className="min-w-0 flex-1">
66+
<h4 className="truncate font-medium text-sm">
67+
{sponsor.sponsor.name || sponsor.sponsor.login}
68+
</h4>
69+
</div>
70+
</div>
71+
))}
7272
</div>
7373
</div>
7474
);

apps/web/src/lib/sponsor-utils.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import type { Sponsor } from "@/lib/types";
2+
3+
export const SPECIAL_SPONSOR_THRESHOLD = 100;
4+
5+
export const getSponsorAmount = (sponsor: Sponsor): number => {
6+
// For past sponsors, return 0
7+
if (sponsor.monthlyDollars === -1) {
8+
return 0;
9+
}
10+
11+
// For one-time sponsors, parse the actual amount from tierName
12+
if (sponsor.isOneTime && sponsor.tierName) {
13+
const match = sponsor.tierName.match(/\$(\d+(?:\.\d+)?)/);
14+
return match ? Number.parseFloat(match[1]) : sponsor.monthlyDollars;
15+
}
16+
17+
// For monthly sponsors, use monthlyDollars
18+
return sponsor.monthlyDollars;
19+
};
20+
21+
export const isSpecialSponsor = (sponsor: Sponsor): boolean => {
22+
const amount = getSponsorAmount(sponsor);
23+
return amount >= SPECIAL_SPONSOR_THRESHOLD;
24+
};
25+
26+
export const sortSponsors = (sponsors: Sponsor[]): Sponsor[] => {
27+
return sponsors.sort((a, b) => {
28+
// Past sponsors (monthlyDollars === -1) go to the end
29+
if (a.monthlyDollars === -1 && b.monthlyDollars !== -1) return 1;
30+
if (a.monthlyDollars !== -1 && b.monthlyDollars === -1) return -1;
31+
32+
// If both are past sponsors, sort by creation date (newest first)
33+
if (a.monthlyDollars === -1 && b.monthlyDollars === -1) {
34+
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
35+
}
36+
37+
// For current sponsors, sort by actual amount (highest first)
38+
const aAmount = getSponsorAmount(a);
39+
const bAmount = getSponsorAmount(b);
40+
if (aAmount !== bAmount) {
41+
return bAmount - aAmount;
42+
}
43+
44+
// If amounts are equal, sort by creation date (oldest first)
45+
return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
46+
});
47+
};
48+
49+
export const sortSpecialSponsors = (sponsors: Sponsor[]): Sponsor[] => {
50+
return sponsors.sort((a, b) => {
51+
// Sort by actual amount (highest first)
52+
const aAmount = getSponsorAmount(a);
53+
const bAmount = getSponsorAmount(b);
54+
if (aAmount !== bAmount) {
55+
return bAmount - aAmount;
56+
}
57+
// If amounts are equal, sort by creation date (oldest first)
58+
return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
59+
});
60+
};
61+
62+
export const filterCurrentSponsors = (sponsors: Sponsor[]): Sponsor[] => {
63+
return sponsors.filter((sponsor) => sponsor.monthlyDollars !== -1);
64+
};
65+
66+
export const filterPastSponsors = (sponsors: Sponsor[]): Sponsor[] => {
67+
return sponsors.filter((sponsor) => sponsor.monthlyDollars === -1);
68+
};
69+
70+
export const filterSpecialSponsors = (sponsors: Sponsor[]): Sponsor[] => {
71+
return sponsors.filter(isSpecialSponsor);
72+
};
73+
74+
export const filterRegularSponsors = (sponsors: Sponsor[]): Sponsor[] => {
75+
return sponsors.filter((sponsor) => !isSpecialSponsor(sponsor));
76+
};
77+
78+
export const getSponsorUrl = (sponsor: Sponsor): string => {
79+
return (
80+
sponsor.sponsor.websiteUrl ||
81+
sponsor.sponsor.linkUrl ||
82+
`https://github.com/${sponsor.sponsor.login}`
83+
);
84+
};
85+
86+
export const formatSponsorUrl = (url: string): string => {
87+
return url?.replace(/^https?:\/\//, "")?.replace(/\/$/, "");
88+
};

0 commit comments

Comments
 (0)