Skip to content

Commit 12cbbe5

Browse files
feat(web): add special sponsors docs sidebar
1 parent e546fc0 commit 12cbbe5

File tree

2 files changed

+81
-2
lines changed

2 files changed

+81
-2
lines changed

apps/web/src/app/docs/layout.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { Banner } from "fumadocs-ui/components/banner";
22
import { DocsLayout, type DocsLayoutProps } from "fumadocs-ui/layouts/docs";
33
import type { ReactNode } from "react";
4-
import { baseOptions, links } from "@/app/layout.config";
4+
import { baseOptions } from "@/app/layout.config";
5+
import { SpecialSponsorBanner } from "@/components/special-sponsor-banner";
56
import { source } from "@/lib/source";
67

78
const docsOptions: DocsLayoutProps = {
89
...baseOptions,
910
tree: source.pageTree,
10-
links: links.filter((link) => "text" in link && link.text !== "Docs"),
11+
links: [],
12+
sidebar: {
13+
banner: <SpecialSponsorBanner />,
14+
},
1115
};
1216

1317
export default function Layout({ children }: { children: ReactNode }) {
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"use client";
2+
import Image from "next/image";
3+
import { useEffect, useState } from "react";
4+
import type { Sponsor } from "@/lib/types";
5+
6+
const isSpecialSponsor = (sponsor: Sponsor) => {
7+
return sponsor.monthlyDollars >= 100;
8+
};
9+
10+
export function SpecialSponsorBanner() {
11+
const [specialSponsor, setSpecialSponsor] = useState<Sponsor | null>(null);
12+
const [loading, setLoading] = useState(true);
13+
14+
useEffect(() => {
15+
fetch("https://sponsors.amanv.dev/sponsors.json")
16+
.then((res) => {
17+
if (!res.ok) throw new Error("Failed to fetch sponsors");
18+
return res.json();
19+
})
20+
.then((data) => {
21+
const sponsorsData = Array.isArray(data) ? data : [];
22+
const specialSponsor = sponsorsData.find(
23+
(sponsor) => sponsor.monthlyDollars > 0 && isSpecialSponsor(sponsor),
24+
);
25+
26+
if (specialSponsor) {
27+
setSpecialSponsor(specialSponsor);
28+
}
29+
setLoading(false);
30+
})
31+
.catch(() => {
32+
setLoading(false);
33+
});
34+
}, []);
35+
36+
if (loading) {
37+
return (
38+
<div className="">
39+
<div className="flex items-center gap-3">
40+
<div className="h-[30px] w-[30px] animate-pulse rounded border border-border bg-muted" />
41+
<div className="min-w-0 flex-1">
42+
<div className="h-4 w-20 animate-pulse rounded bg-muted" />
43+
</div>
44+
</div>
45+
</div>
46+
);
47+
}
48+
49+
if (!specialSponsor) {
50+
return null;
51+
}
52+
53+
return (
54+
<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>
72+
</div>
73+
</div>
74+
);
75+
}

0 commit comments

Comments
 (0)