diff --git a/src/components/TopMonthlySponsors.astro b/src/components/TopMonthlySponsors.astro new file mode 100644 index 00000000..4086d8b0 --- /dev/null +++ b/src/components/TopMonthlySponsors.astro @@ -0,0 +1,83 @@ +--- +import featuredSponsors from "../featured-sponsors.json" + +/** + * Top-N featured organizations by tracked monthly contribution. + * + * Data comes from `src/featured-sponsors.json`. Entries that have an + * explicit `monthlyEquivalent` field are sorted descending and included + * in this list. Entries without that field are not shown (they still + * render in `` and in the all-sponsors bubble grid). + * + * The set of tracked entries will grow as `monthlyEquivalent` values are + * filled in. The current floor is $200/month, which matches the major-tier + * GitHub Sponsors threshold. + */ +type Sponsor = { + name: string + type: string + logo: string + darklogo: string + squareLogo: string + url: string + github?: string + isLeading?: boolean + monthlyEquivalent?: number +} + +const sponsors = featuredSponsors as Sponsor[] + +interface Props { + /** Maximum number of sponsors to show. Defaults to 10. */ + limit?: number +} + +const { limit = 10 } = Astro.props as Props + +const tracked = sponsors + .filter((s): s is Sponsor & { monthlyEquivalent: number } => + typeof s.monthlyEquivalent === "number" && s.monthlyEquivalent > 0, + ) + .slice() + .sort((a, b) => b.monthlyEquivalent - a.monthlyEquivalent) + .slice(0, limit) + +const usd = (n: number) => + `$${n.toLocaleString("en-US", { maximumFractionDigits: 0 })}` +--- + +{tracked.length > 0 && ( +
+ {tracked.map((sponsor, idx) => ( + + + #{idx + 1} + +
+ {`${sponsor.name} + +
+

+ {sponsor.name} +

+

+ {usd(sponsor.monthlyEquivalent)}+/mo +

+
+ ))} +
+)} diff --git a/src/featured-sponsors.json b/src/featured-sponsors.json index 7c510909..5ff8d71c 100644 --- a/src/featured-sponsors.json +++ b/src/featured-sponsors.json @@ -6,7 +6,8 @@ "darklogo": "/logos/upsun-darkmode.svg", "squareLogo": "/logos/upsun-square.svg", "url": "https://upsun.com", - "isLeading": false + "isLeading": false, + "monthlyEquivalent": 200 }, { "name": "Tag1", @@ -15,7 +16,8 @@ "darklogo": "/logos/dark-tag1.svg", "squareLogo": "/logos/tag1-square.svg", "url": "https://tag1.com", - "isLeading": false + "isLeading": false, + "monthlyEquivalent": 200 }, { "name": "i-gelb", @@ -25,7 +27,8 @@ "squareLogo": "/logos/i-gelb-square.svg", "url": "https://i-gelb.net", "github": "i-gelb", - "isLeading": false + "isLeading": false, + "monthlyEquivalent": 200 }, { "name": "Institute for Advanced Study", @@ -34,7 +37,8 @@ "darklogo": "/logos/ias-dark.svg", "squareLogo": "/logos/ias-square.svg", "url": "https://www.ias.edu/", - "isLeading": false + "isLeading": false, + "monthlyEquivalent": 200 }, { "name": "b13", diff --git a/src/pages/sponsor.astro b/src/pages/sponsor.astro new file mode 100644 index 00000000..cb14ee63 --- /dev/null +++ b/src/pages/sponsor.astro @@ -0,0 +1,443 @@ +--- +import Layout from "../layouts/Layout.astro" +import Heading from "../components/Heading.astro" +import CtaButton from "../components/CtaButton.astro" +import FeaturedSponsors from "../components/FeaturedSponsors.astro" +import TopMonthlySponsors from "../components/TopMonthlySponsors.astro" +import Sponsors from "../components/Sponsors.astro" + +const title = `Sponsor DDEV` +const description = `Help fund DDEV's two full-time maintainers. Monthly recurring sponsorship via GitHub, PayPal, or invoice — live progress, transparent breakdown.` + +/** + * Live sponsorship totals from the published GitHub Pages snapshot at + * https://ddev.github.io/sponsorship-data/data/all-sponsorships.json + * (alias: https://ddev.com/s/sponsorship-data.json). Updated daily by + * the github-sponsorships.sh script in ddev/sponsorship-data. + * + * Fetched at build time. If the endpoint is unreachable we render the + * page with the most-recent known totals so the build never breaks on a + * transient network failure. + */ +const SPONSORSHIP_DATA_URL = + "https://ddev.github.io/sponsorship-data/data/all-sponsorships.json" + +interface SourceTotals { + monthly: number + sponsors: number +} + +interface SponsorshipSnapshot { + github: SourceTotals + monthly_invoiced: SourceTotals + annual_invoiced_monthly_equivalent: SourceTotals + paypal_monthly: number + total_monthly: number + goal_amount: number + progress_percentage: number + one_week_ago_monthly: number | null + updated_iso: string | null + fallback: boolean +} + +const fallback: SponsorshipSnapshot = { + github: { monthly: 5484, sponsors: 239 }, + monthly_invoiced: { monthly: 3048, sponsors: 8 }, + annual_invoiced_monthly_equivalent: { monthly: 879, sponsors: 9 }, + paypal_monthly: 45, + total_monthly: 9456, + goal_amount: 12000, + progress_percentage: 78.8, + one_week_ago_monthly: 7853, + updated_iso: null, + fallback: true, +} + +async function fetchSponsorshipData(): Promise { + try { + const res = await fetch(SPONSORSHIP_DATA_URL, { + signal: AbortSignal.timeout(8000), + }) + if (!res.ok) return fallback + const data: any = await res.json() + const ghDdev = data.github_ddev_sponsorships ?? {} + const ghRfay = data.github_rfay_sponsorships ?? {} + const mi = data.monthly_invoiced_sponsorships ?? {} + const ai = data.annual_invoiced_sponsorships ?? {} + return { + github: { + monthly: + (ghDdev.total_monthly_sponsorship ?? 0) + + (ghRfay.total_monthly_sponsorship ?? 0), + sponsors: (ghDdev.total_sponsors ?? 0) + (ghRfay.total_sponsors ?? 0), + }, + monthly_invoiced: { + monthly: mi.total_monthly_sponsorship ?? 0, + sponsors: mi.total_sponsors ?? 0, + }, + annual_invoiced_monthly_equivalent: { + monthly: ai.monthly_equivalent_sponsorship ?? 0, + sponsors: ai.total_sponsors ?? 0, + }, + paypal_monthly: data.paypal_sponsorships ?? 0, + total_monthly: data.total_monthly_average_income ?? 0, + goal_amount: data.current_goal?.target_amount ?? 12000, + progress_percentage: data.current_goal?.progress_percentage ?? 0, + one_week_ago_monthly: data.historical_data?.one_week_ago ?? null, + updated_iso: data.updated_datetime ?? null, + fallback: false, + } + } catch { + return fallback + } +} + +const data = await fetchSponsorshipData() + +const usd = (n: number) => + `$${n.toLocaleString("en-US", { maximumFractionDigits: 0 })}` + +const sevenDayDelta = + data.one_week_ago_monthly != null + ? data.total_monthly - data.one_week_ago_monthly + : null + +const updatedHuman = data.updated_iso + ? new Date(data.updated_iso).toLocaleDateString("en-US", { + year: "numeric", + month: "long", + day: "numeric", + }) + : null + +// Clamp progress for the bar; the real percentage is also shown numerically. +const barPercent = Math.min(100, Math.max(0, data.progress_percentage)) + +const sources = [ + { + name: "GitHub Sponsors", + amount: data.github.monthly, + sponsors: data.github.sponsors, + note: "ddev org + rfay", + url: "https://github.com/sponsors/ddev", + }, + { + name: "Monthly invoiced", + amount: data.monthly_invoiced.monthly, + sponsors: data.monthly_invoiced.sponsors, + note: "direct billing", + url: "/contact", + }, + { + name: "Annual invoiced", + amount: data.annual_invoiced_monthly_equivalent.monthly, + sponsors: data.annual_invoiced_monthly_equivalent.sponsors, + note: "monthly equivalent", + url: "/contact", + }, + { + name: "PayPal", + amount: data.paypal_monthly, + sponsors: null, + note: "monthly equivalent", + url: "https://www.paypal.com/donate/?hosted_button_id=MCNCSZHC7LHSQ", + }, +] +--- + + +
+ + + {/* --- Live progress block --- */} +
+
+
+

+ Monthly recurring sponsorship +

+

+ {usd(data.total_monthly)} + +  / {usd(data.goal_amount)} + +

+
+

+ + {data.progress_percentage.toFixed(1)}% + + of the {usd(data.goal_amount)}/month goal + {sevenDayDelta != null && sevenDayDelta !== 0 && ( + <> +
+ + {sevenDayDelta > 0 ? "+" : ""} + {usd(sevenDayDelta)} vs. one week ago + + + )} +

+
+ +
+
+
+
+ + {updatedHuman && !data.fallback && ( +

+ Data updated daily · last update {updatedHuman} ·{" "} + + view raw JSON + +

+ )} +
+ + {/* --- Three primary CTAs --- */} +
+

+ Three ways to sponsor +

+

+ Any amount helps. Monthly recurring is what funds the maintainers; we + list each channel separately so you can pick the one that fits. +

+ +
+ {/* GitHub Sponsors */} +
+

+ GitHub Sponsors +

+

+ For individuals and organizations who already use GitHub. Monthly + recurring; tiers from $1 to $530. +

+ +
+ + {/* PayPal */} +
+

+ PayPal +

+

+ One-time gift or recurring without a GitHub account. Supports any + amount. +

+ +
+ + {/* Invoice */} +
+

+ Direct invoice +

+

+ For organizations with a procurement process. The DDEV Foundation + is a US 501(c)(3); invoices and W-9 available on request. +

+ +
+
+
+ + {/* --- Source breakdown table --- */} +
+

+ Where the {usd(data.total_monthly)} comes from +

+

+ Monthly recurring equivalent, by source. Annual sponsorships are + normalized to a monthly figure. +

+ +
+ + + + + + + + + + + {sources.map((s) => ( + + + + + + + ))} + + + + + + + +
Source + Monthly + + Sponsors +
+ + {s.name} + + + {usd(s.amount)} + + {s.sponsors != null ? s.sponsors : "—"} +
Total + {usd(data.total_monthly)} + + — +
+
+
+ + {/* --- Top monthly sponsors (tracked) --- */} +
+

+ Top monthly sponsors +

+

+ Featured organizations sorted by tracked monthly contribution. Only + entries with a confirmed amount in + + featured-sponsors.json + + are listed here; the full set is below. +

+ +
+ + {/* --- Featured organizations --- */} +
+ +

+ Companies whose support keeps DDEV maintained. Want your logo here? + Sponsor at the major tier (≥ $200/month on GitHub Sponsors, or contact + us for invoicing). +

+ +
+ + {/* --- Bubble grid of all sponsors --- */} +
+

+ All current sponsors +

+

+ Every account currently sponsoring DDEV on GitHub, plus our featured + organizations. +

+ +
+ + {/* --- Closing CTAs + foundation note --- */} +
+
+
+ + +
+ +
+ +

+ The DDEV Foundation is a Colorado, USA 501(c)(3) non-profit. See{" "} + + DDEV Foundation + {" "} + for board, financials, and tax status. +

+
+
+
diff --git a/src/pages/support-ddev.astro b/src/pages/support-ddev.astro index 45b12751..ad683c6f 100644 --- a/src/pages/support-ddev.astro +++ b/src/pages/support-ddev.astro @@ -17,6 +17,18 @@ const title = `Support DDEV`
+
+

+ Looking to sponsor financially? + See ddev.com/sponsor + for the three sponsorship channels (GitHub, PayPal, invoice), a live + breakdown of where the monthly recurring total comes from, and the + current progress toward our $12,000/month goal. This page covers the + rest: community contributions, spreading the word, and other ways to + help. +

+
+