docs: Community Landing Page (#2869)

Co-authored-by: Tania Chakraborty <tchakraborty@block.xyz>
Co-authored-by: Anthony Giuliano <agiuliano@squareup.com>
This commit is contained in:
taniashiba
2025-06-16 17:33:53 -04:00
committed by GitHub
parent aa0985328d
commit 516e5fed7a
7 changed files with 626 additions and 0 deletions

View File

@@ -196,6 +196,10 @@ const config: Config = {
{
title: "Community",
items: [
{
label: "Spotlight",
to: "community",
},
{
label: "Discord",
href: "https://discord.gg/block-opensource",

View File

@@ -0,0 +1,120 @@
# Community All Stars - Monthly Update Guide
This directory contains the data files for the Community All Stars section on the community page.
## Monthly Update Process
### Step 1: Create New Month Data File
1. Copy `template.json` to create a new file named `MONTH-YEAR.json` (e.g., `june-2025.json`)
2. Update the data in the new file:
```json
{
"month": "June 2025",
"featuredContributors": [
{
"name": "John Doe",
"handle": "johndoe"
}
],
"risingStars": [
{
"name": "Jane Smith",
"handle": "janesmith"
}
],
"leaderboard": [
{ "handle": "johndoe", "rank": 1, "medal": "🥇" },
{ "handle": "janesmith", "rank": 2, "medal": "🥈" },
{ "handle": "contributor3", "rank": 3, "medal": "🥉" },
{ "handle": "contributor4", "rank": 4 }
]
}
```
### Step 2: Update Configuration
1. Open `config.json`
2. Add the new month to the `availableMonths` array:
```json
{
"availableMonths": [
{
"id": "june-2025",
"display": "June 2025",
"file": "june-2025.json"
}
],
"defaultMonth": "june-2025"
}
```
3. Update `defaultMonth` to the new month's ID
### Step 3: Update Code Imports
1. Open `../pages/community.tsx`
2. Add import for the new data file:
```typescript
import june2025Data from "../data/community/june-2025.json";
```
3. Add the new data to the `communityDataMap`:
```typescript
const communityDataMap = {
"june-2025": june2025Data,
// ... other months
};
```
## Data Format
### Community Stars & Team Stars
- `name`: Full display name
- `handle`: GitHub username (without @)
### Monthly Leaderboard
- `handle`: GitHub username (without @)
- `rank`: Position number (1, 2, 3, etc.)
- `medal`: Only for top 3 ("🥇", "🥈", "🥉")
## Section Mapping
The JSON data maps to these page sections:
- `featuredContributors`**Community Stars** section
- `risingStars`**Team Stars** section
- `leaderboard`**Monthly Leaderboard** section
## Tips
- Avatar images are automatically generated from GitHub usernames
- GitHub links are automatically created using the handle
- The medal field is optional - only include for top 3 positions
- You can have any number of leaderboard entries
- Names and handles are case-sensitive
## File Structure
```
community/
├── config.json # Main configuration
├── template.json # Template for new months
├── april-2025.json # April 2025 data
├── may-2025.json # May 2025 data
└── README.md # This file
```
## Quick Monthly Checklist
- [ ] Copy template.json to new month file
- [ ] Fill in contributor data for Community Stars
- [ ] Fill in contributor data for Team Stars
- [ ] Update Monthly Leaderboard rankings
- [ ] Update config.json with new month
- [ ] Add import to community.tsx
- [ ] Add to communityDataMap
- [ ] Test the page locally

View File

@@ -0,0 +1,83 @@
{
"month": "April 2025",
"communityStars": [
{
"name": "Vinny Fiano",
"handle": "ynniv"
},
{
"name": "Carine B",
"handle": "TBD",
"avatarUrl": "https://cdn.discordapp.com/avatars/1177778226957926540/186b0898f793e7292a5b385de35a19fc?size=1024"
},
{
"name": "Best Codes",
"handle": "The-Best-Codes"
},
{
"name": "comoyomalove",
"handle": "TBD",
"avatarUrl": "https://cdn.discordapp.com/avatars/552168841992732673/2d88c30d3e0fc3fda6a3815b819f4546?size=1024"
},
{
"name": "jokellyfenton",
"handle": "jokellyfenton"
}
],
"teamStars": [
{
"name": "Shea Craig",
"handle": "sheagcraig"
},
{
"name": "Jim Bennett",
"handle": "jimbobbennett"
},
{
"name": "Oliver",
"handle": "opdich"
}
],
"leaderboard": [
{ "handle": "yingjiehe-xyz", "rank": 1, "medal": "🥇" },
{ "handle": "zanesq", "rank": 2, "medal": "🥈" },
{ "handle": "angiejones", "rank": 3, "medal": "🥉" },
{ "handle": "kalvinnchau", "rank": 4 },
{ "handle": "lily-de", "rank": 5 },
{ "handle": "blackgirlbytes", "rank": 6 },
{ "handle": "alexhancock", "rank": 7 },
{ "handle": "zakiali", "rank": 8 },
{ "handle": "salman1993", "rank": 9 },
{ "handle": "EbonyLouis", "rank": 10 },
{ "handle": "iandouglas", "rank": 11 },
{ "handle": "michaelneale", "rank": 12 },
{ "handle": "JohnMAustin78", "rank": 13 },
{ "handle": "wendytang", "rank": 14 },
{ "handle": "Kvadratni", "rank": 15 },
{ "handle": "ahau-square", "rank": 16 },
{ "handle": "nahiyankhan", "rank": 17 },
{ "handle": "The-Best-Codes", "rank": 18 },
{ "handle": "baxen", "rank": 19 },
{ "handle": "laanak08", "rank": 20 },
{ "handle": "agiuliano-square", "rank": 21 },
{ "handle": "acekyd", "rank": 22 },
{ "handle": "taniashiba", "rank": 23 },
{ "handle": "dbraduan", "rank": 24 },
{ "handle": "sheagcraig", "rank": 25 },
{ "handle": "sana-db", "rank": 26 },
{ "handle": "jimbobbennett", "rank": 27 },
{ "handle": "lifeizhou-ap", "rank": 28 },
{ "handle": "alicehau", "rank": 29 },
{ "handle": "opdich", "rank": 30 },
{ "handle": "rockwotj", "rank": 31 },
{ "handle": "alexgleason", "rank": 32 },
{ "handle": "meenalc", "rank": 33 },
{ "handle": "ArtBears", "rank": 34 },
{ "handle": "GlyneG", "rank": 35 },
{ "handle": "DOsinga", "rank": 36 },
{ "handle": "omo", "rank": 37 },
{ "handle": "allisonjoycarter", "rank": 38 },
{ "handle": "mpaquettePax8", "rank": 39 },
{ "handle": "capttrousers", "rank": 40 }
]
}

View File

@@ -0,0 +1,15 @@
{
"availableMonths": [
{
"id": "april-2025",
"display": "April 2025",
"file": "april-2025.json"
},
{
"id": "may-2025",
"display": "May 2025",
"file": "may-2025.json"
}
],
"defaultMonth": "may-2025"
}

View File

@@ -0,0 +1,84 @@
{
"month": "May 2025",
"communityStars": [
{
"name": "GitMurf",
"handle": "GitMurf"
},
{
"name": "Anil Muppalla",
"handle": "anilmuppalla"
},
{
"name": "Ben Walding",
"handle": "bwalding"
},
{
"name": "Gustavo Hoirisch",
"handle": "gugahoi"
},
{
"name": "Antonio Cheong",
"handle": "acheong08"
}
],
"teamStars": [
{
"name": "Oliver",
"handle": "opdich"
},
{
"name": "Jack Amadeo",
"handle": "jamadeo"
},
{
"name": "Shea Craig",
"handle": "sheagcraig"
}
],
"leaderboard": [
{ "handle": "blackgirlbytes", "rank": 1, "medal": "🥇" },
{ "handle": "zanesq", "rank": 2, "medal": "🥈" },
{ "handle": "michaelneale", "rank": 3, "medal": "🥉" },
{ "handle": "angiejones", "rank": 4 },
{ "handle": "Kvadratni", "rank": 5 },
{ "handle": "lifeizhou-ap", "rank": 6 },
{ "handle": "dianed-square", "rank": 7 },
{ "handle": "yingjiehe-xyz", "rank": 8 },
{ "handle": "salman1993", "rank": 9 },
{ "handle": "ahau-square", "rank": 10 },
{ "handle": "iandouglas", "rank": 11 },
{ "handle": "emma-squared", "rank": 12 },
{ "handle": "dbraduan", "rank": 13 },
{ "handle": "lily-de", "rank": 14 },
{ "handle": "alexhancock", "rank": 15 },
{ "handle": "EbonyLouis", "rank": 16 },
{ "handle": "wendytang", "rank": 17 },
{ "handle": "The-Best-Codes", "rank": 18 },
{ "handle": "opdich", "rank": 19 },
{ "handle": "agiuliano-square", "rank": 20 },
{ "handle": "patrickReiis", "rank": 21 },
{ "handle": "kalvinnchau", "rank": 22 },
{ "handle": "acekyd", "rank": 23 },
{ "handle": "nahiyankhan", "rank": 24 },
{ "handle": "taniashiba", "rank": 25 },
{ "handle": "JohnMAustin78", "rank": 26 },
{ "handle": "sheagcraig", "rank": 27 },
{ "handle": "alicehau", "rank": 28 },
{ "handle": "bwalding", "rank": 29 },
{ "handle": "jamadeo", "rank": 30 },
{ "handle": "rockwotj", "rank": 31 },
{ "handle": "danielzayas", "rank": 32 },
{ "handle": "svenstaro", "rank": 33 },
{ "handle": "adaug", "rank": 34 },
{ "handle": "loganmoseley", "rank": 35 },
{ "handle": "tiborvass", "rank": 36 },
{ "handle": "xuv", "rank": 37 },
{ "handle": "anilmuppalla", "rank": 38 },
{ "handle": "spencrmartin", "rank": 39 },
{ "handle": "gknoblauch", "rank": 40 },
{ "handle": "acheong08", "rank": 41 },
{ "handle": "faces-of-eth", "rank": 42 },
{ "handle": "wesbillman", "rank": 43 }
]
}

View File

@@ -0,0 +1,54 @@
{
"month": "MONTH YEAR",
"communityStars": [
{
"name": "Full Name 1",
"handle": "github-username-1"
},
{
"name": "Full Name 2",
"handle": "github-username-2"
},
{
"name": "Full Name 3",
"handle": "github-username-3"
},
{
"name": "Full Name 4",
"handle": "github-username-4"
},
{
"name": "Full Name 5",
"handle": "github-username-5"
}
],
"teamStars": [
{
"name": "Full Name 1",
"handle": "github-username-1"
},
{
"name": "Full Name 2",
"handle": "github-username-2"
},
{
"name": "Full Name 3",
"handle": "github-username-3"
},
{
"name": "Full Name 4",
"handle": "github-username-4"
},
{
"name": "Full Name 5",
"handle": "github-username-5"
}
],
"leaderboard": [
{ "handle": "github-username-1", "rank": 1, "medal": "🥇" },
{ "handle": "github-username-2", "rank": 2, "medal": "🥈" },
{ "handle": "github-username-3", "rank": 3, "medal": "🥉" },
{ "handle": "github-username-4", "rank": 4 },
{ "handle": "github-username-5", "rank": 5 }
]
}

View File

@@ -0,0 +1,266 @@
import type { ReactNode } from "react";
import React from "react";
import Link from "@docusaurus/Link";
import Layout from "@theme/Layout";
import Heading from "@theme/Heading";
// Import community data
import communityConfig from "./data/config.json";
import april2025Data from "./data/april-2025.json";
import may2025Data from "./data/may-2025.json";
// Create a data map for easy access
const communityDataMap = {
"april-2025": april2025Data,
"may-2025": may2025Data,
};
function UpcomingEventsSection() {
return (
<section className="w-full flex flex-col items-center gap-8 my-8">
<div className="text-center">
<Heading as="h1">Upcoming Events</Heading>
<p>Join us for livestreams, workshops, and discussions about goose and open source projects.</p>
</div>
{/* Embedded Calendar */}
<iframe
src="https://calget.com/c/t7jszrie"
className="w-full h-[600px] border-0 rounded-lg"
title="Goose Community Calendar"
/>
{/* Call to Action */}
<p className="italic text-textStandard">
Want to join us on a livestream or have ideas for future events?
Reach out to the team on <Link href="https://discord.gg/block-opensource">Discord</Link>.
</p>
</section>
);
}
function CommunityAllStarsSection() {
const [activeMonth, setActiveMonth] = React.useState(communityConfig.defaultMonth);
const [showScrollIndicator, setShowScrollIndicator] = React.useState(true);
const currentData = communityDataMap[activeMonth];
// Handle scroll to show/hide indicator
const handleScroll = (e) => {
const { scrollTop, scrollHeight, clientHeight } = e.target;
const isAtBottom = scrollTop + clientHeight >= scrollHeight - 10; // 10px threshold
setShowScrollIndicator(!isAtBottom);
};
return (
<section className="w-full flex flex-col items-center gap-8 my-8">
<div className="text-center">
<Heading as="h1">Community All Stars</Heading>
<p>Every month, we take a moment and celebrate the open source community. Here are the top contributors and community champions!</p>
</div>
{/* Month Tabs */}
<div className="flex justify-center gap-2 flex-wrap">
{communityConfig.availableMonths.map((month) => (
<button
key={month.id}
className="button button--primary"
onClick={() => setActiveMonth(month.id)}
style={activeMonth === month.id ? {
border: '3px solid var(--ifm-color-primary-dark)',
boxShadow: '0 2px 8px rgba(0,0,0,0.15)'
} : {}}
>
{activeMonth === month.id ? '📅 ' : ''}{month.display}
</button>
))}
</div>
{/* Community Stars */}
<div className="text-center">
<Heading as="h3"> Community Stars</Heading>
<p className="text-sm text-textStandard">
Top 5 Contributors from the open source community!
</p>
</div>
<div className="flex justify-center">
{currentData.communityStars.map((contributor, index) => (
<StarsCard key={index} contributor={contributor} />
))}
</div>
{/* Team Stars */}
<div className="text-center">
<Heading as="h3"> Team Stars</Heading>
<p className="text-sm text-textStandard">
Top 5 Contributors from all Block teams!
</p>
</div>
<div className="flex justify-center">
{currentData.teamStars.map((contributor, index) => (
<StarsCard key={index} contributor={{...contributor, totalCount: currentData.teamStars.length}} />
))}
</div>
{/* Monthly Leaderboard */}
<div className="text-center">
<Heading as="h3">🏆 Monthly Leaderboard</Heading>
<p className="text-sm text-textStandard">
Rankings of all goose contributors getting loose this month!
</p>
</div>
<div className="card w-full max-w-xl p-5 relative">
<div
className="flex flex-col gap-2 text-sm max-h-[550px] overflow-y-auto pr-2"
onScroll={handleScroll}
>
{currentData.leaderboard.map((contributor, index) => {
const isTopContributor = index < 3; // Top 3 contributors
const bgColor = index === 0 ? 'bg-yellow-400' :
index === 1 ? 'bg-gray-300' :
index === 2 ? 'bg-yellow-600' : null;
return (
<div
key={index}
className={`flex items-center p-3 rounded-lg font-medium cursor-pointer transition-all duration-200 hover:-translate-y-0.5 ${
isTopContributor
? `${bgColor} font-bold shadow-md hover:shadow-lg`
: 'bg-bgSubtle border border-borderStandard hover:bg-bgApp hover:shadow-md'
}`}
>
{contributor.medal && (
<span className="mr-3 text-lg">
{contributor.medal}
</span>
)}
<span className={`mr-3 min-w-[30px] ${isTopContributor ? 'text-base text-black' : 'text-sm'}`}>
{contributor.rank}.
</span>
{contributor.handle !== 'TBD' ? (
<Link
href={`https://github.com/${contributor.handle}`}
className={`${isTopContributor ? 'text-black text-base' : 'text-inherit text-sm'}`}
>
@{contributor.handle}
</Link>
) : (
<span className="text-textSubtle italic">
@TBD
</span>
)}
</div>
);
})}
</div>
{/* Simple scroll indicator - only show when not at bottom */}
{showScrollIndicator && (
<div className="absolute bottom-5 inset-x-0 flex justify-center">
<span className="w-fit text-xs bg-bgProminent p-2 rounded-full font-medium pointer-events-none flex items-center gap-1.5">
Scroll for more
</span>
</div>
)}
</div>
<div className="text-center">
<p>
Thank you all for contributing!
</p>
</div>
{/* Want to be featured section */}
<div className="text-center">
<Heading as="h2">Want to be featured?</Heading>
</div>
<div className="card max-w-xl">
<div className="card__header text-center">
<div className="avatar avatar--vertical">
<div className="w-16 h-16 rounded-full bg-blue-400 flex items-center justify-center text-2xl text-blue-500">
</div>
</div>
</div>
<div className="card__body text--center">
<div className="mb-4">
<strong>Your Name Here</strong>
<br />
<small>Future Community Star</small>
</div>
<div className="text-sm">
Want to be a Community All Star? Just start contributing on{' '}
<Link href="https://github.com/block/goose">GitHub</Link>, helping others on{' '}
<Link href="https://discord.gg/block-opensource">Discord</Link>, or share your
goose projects with the community! You can check out the{' '}
<Link href="https://github.com/block/goose/blob/main/CONTRIBUTING.md">contributing guide</Link>{' '}
for more tips.
</div>
</div>
</div>
</section>
);
}
export function StarsCard({contributor}): ReactNode {
return (
<div className={`col ${contributor.totalCount <= 3 ? 'col--4' : 'col--2'} mb-8`}>
<div
className="h-full border-2 border-borderSubtle rounded-2xl cursor-pointer hover:shadow-xl hover:border-[var(--ifm-color-primary-dark)]"
>
<div className="card__header text-center">
<div className="avatar avatar--vertical">
{contributor.avatarUrl ? (
<img
className="avatar__photo avatar__photo--lg"
src={contributor.avatarUrl}
alt={contributor.name}
/>
) : contributor.handle !== 'TBD' ? (
<img
className="avatar__photo avatar__photo--lg"
src={`https://github.com/${contributor.handle}.png`}
alt={contributor.name}
/>
) : (
<div className="w-16 h-16 rounded-full bg-gray-200 flex items-center justify-center text-xl text-textSubtle">
?
</div>
)}
</div>
</div>
<div className="card__body text-center">
<div className="mb-2">
<strong>
{contributor.handle !== 'TBD' ? (
<Link href={`https://github.com/${contributor.handle}`}>
{contributor.name} (@{contributor.handle})
</Link>
) : (
`${contributor.name}`
)}
</strong>
</div>
</div>
</div>
</div>
);
};
export default function Community(): ReactNode {
return (
<Layout
title="Community"
description="Join the Goose community - connect with developers, contribute to the project, and help shape the future of AI-powered development tools."
>
<main className="container">
<UpcomingEventsSection />
<CommunityAllStarsSection />
</main>
</Layout>
);
}