From ae825b39dda264d06f6c1d90b5abe1e42b7cd2dd Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Wed, 14 May 2025 20:28:07 +0530 Subject: [PATCH] add sponsors section in home page and stack builder --- .../(home)/_components/sponsors-section.tsx | 156 +++++++++++++----- .../app/(home)/_components/stack-builder.tsx | 91 ++++++++++ apps/web/src/app/(home)/page.tsx | 7 +- apps/web/src/lib/types.ts | 17 ++ bun.lock | 2 +- 5 files changed, 230 insertions(+), 43 deletions(-) diff --git a/apps/web/src/app/(home)/_components/sponsors-section.tsx b/apps/web/src/app/(home)/_components/sponsors-section.tsx index 47e8570..a7870d6 100644 --- a/apps/web/src/app/(home)/_components/sponsors-section.tsx +++ b/apps/web/src/app/(home)/_components/sponsors-section.tsx @@ -1,59 +1,139 @@ "use client"; -import { motion } from "motion/react"; +import type { Sponsor } from "@/lib/types"; import Image from "next/image"; +import { useEffect, useState } from "react"; export default function SponsorsSection() { - const sectionVariants = { - hidden: { opacity: 0, y: 30 }, - visible: { - opacity: 1, - y: 0, - transition: { duration: 0.6, ease: "easeOut" }, - }, - }; + const [sponsors, setSponsors] = useState([]); + const [loadingSponsors, setLoadingSponsors] = useState(true); + const [sponsorError, setSponsorError] = useState(null); + + useEffect(() => { + fetch( + "https://cdn.jsdelivr.net/gh/amanvarshney01/sponsors/sponsorkit/sponsors.json", + ) + .then((res) => { + if (!res.ok) throw new Error("Failed to fetch sponsors"); + return res.json(); + }) + .then((data) => { + setSponsors(Array.isArray(data) ? data : []); + setLoadingSponsors(false); + }) + .catch(() => { + setSponsorError("Could not load sponsors"); + setLoadingSponsors(false); + }); + }, []); return ( - -
+
+

Sponsors

+

+ Thank you to our sponsors for supporting this project! +

- - - Sponsors - - -
+ {loadingSponsors ? ( +
+ Loading sponsors... +
+ ) : sponsorError ? ( +
+ {sponsorError} +
+ ) : sponsors.length === 0 ? ( +
+ No sponsors yet.{" "} + + Become a Sponsor + +
+ ) : ( +
+ {sponsors.map((entry) => { + const since = new Date(entry.createdAt).toLocaleDateString(); + const title = `@${entry.sponsor.login}\n${entry.sponsor.type}${ + entry.isOneTime ? " (One-time)" : " (Monthly)" + }\n${entry.tierName ? `${entry.tierName}\n` : ""}Since: ${since}`; + return ( + +
+ {entry.sponsor.name +
+ + {entry.sponsor.name || entry.sponsor.login} + + {entry.tierName && ( + + {entry.tierName} + + )} + + {entry.monthlyDollars > 0 + ? `$${entry.monthlyDollars} / mo` + : entry.isOneTime && entry.monthlyDollars > 0 + ? `$${entry.monthlyDollars} one-time` + : "Supporter"} + + + {entry.isOneTime ? "One-time" : "Monthly"} + +
+ ); + })} +
+ )} + - +
); } diff --git a/apps/web/src/app/(home)/_components/stack-builder.tsx b/apps/web/src/app/(home)/_components/stack-builder.tsx index f8b6705..9028d82 100644 --- a/apps/web/src/app/(home)/_components/stack-builder.tsx +++ b/apps/web/src/app/(home)/_components/stack-builder.tsx @@ -16,6 +16,7 @@ import { isStackDefault, } from "@/lib/constant"; import { stackParsers, stackQueryStatesOptions } from "@/lib/stack-url-state"; +import type { Sponsor } from "@/lib/types"; import { cn } from "@/lib/utils"; import { Check, @@ -890,6 +891,9 @@ const StackBuilder = () => { const [, setLastChanges] = useState< Array<{ category: string; message: string }> >([]); + const [sponsors, setSponsors] = useState([]); + const [loadingSponsors, setLoadingSponsors] = useState(true); + const [sponsorError, setSponsorError] = useState(null); const sectionRefs = useRef>({}); const contentRef = useRef(null); @@ -1416,6 +1420,24 @@ const StackBuilder = () => { setProjectNameError(validateProjectName(stack.projectName || "")); }, [stack.projectName]); + useEffect(() => { + fetch( + "https://cdn.jsdelivr.net/gh/amanvarshney01/sponsors/sponsorkit/sponsors.json", + ) + .then((res) => { + if (!res.ok) throw new Error("Failed to fetch sponsors"); + return res.json(); + }) + .then((data) => { + setSponsors(Array.isArray(data) ? data : []); + setLoadingSponsors(false); + }) + .catch(() => { + setSponsorError("Could not load sponsors"); + setLoadingSponsors(false); + }); + }, []); + const handleTechSelect = ( category: keyof typeof TECH_OPTIONS, techId: string, @@ -1873,6 +1895,75 @@ const StackBuilder = () => { ); })}
+ {/* Sponsors Section */} +
+
+

+ Sponsors +

+
+ {loadingSponsors ? ( +
+ Loading sponsors... +
+ ) : sponsorError ? ( +
+ {sponsorError} +
+ ) : sponsors.length === 0 ? ( +
+ No sponsors yet.{" "} + + Become a Sponsor + +
+ ) : ( + + )} + +
diff --git a/apps/web/src/app/(home)/page.tsx b/apps/web/src/app/(home)/page.tsx index 9928183..d86bfa3 100644 --- a/apps/web/src/app/(home)/page.tsx +++ b/apps/web/src/app/(home)/page.tsx @@ -2,13 +2,12 @@ import ShinyText from "@/app/(home)/_components/shiny-text"; import { cn } from "@/lib/utils"; import { motion } from "motion/react"; -import React from "react"; import CodeContainer from "./_components/code-container"; import CustomizableSection from "./_components/customizable-section"; import Footer from "./_components/footer"; import Navbar from "./_components/navbar"; import NpmPackage from "./_components/npm-package"; -// import SponsorsSection from "./_components/SponsorsSection"; +import SponsorsSection from "./_components/sponsors-section"; import Testimonials from "./_components/testimonials"; export default function HomePage() { @@ -161,7 +160,7 @@ export default function HomePage() { - {/* - */} +