Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions documentation/src/pages/community/data/community-content.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"title": "🎃 Community Content Contributions",
"description": "Amazing blog posts, videos, and tutorials created by our community for goose!",
"lastUpdated": "2025-10-02",
"hacktoberfestYear": 2025,
"submissionUrl": "https://github.com/block/goose/issues/new?template=hacktoberfest-content-submission.yml",
"submissions": [
{
"title": "Configuring goose for Team Environments and Shared Workflows",
"author": {
"name": "Community Contributor",
"handle": "teamgoose"
},
"type": "blog",
"url": "https://teamgoose.dev/goose-team-setup",
"thumbnail": "https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=400&h=225&fit=crop&crop=entropy&auto=format",
"submittedDate": "2025-10-20",
"tags": ["team-setup", "configuration", "collaboration"]
}
]
}
172 changes: 171 additions & 1 deletion documentation/src/pages/community/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import may2025Data from "./data/may-2025.json";
import june2025Data from "./data/june-2025.json";
import july2025Data from "./data/july-2025.json";
import august2025Data from "./data/august-2025.json";
import communityContentData from "./data/community-content.json";

// Create a data map for easy access
const communityDataMap = {
Expand Down Expand Up @@ -211,6 +212,174 @@ function CommunityAllStarsSection() {
);
}

function CommunityContentSpotlightSection() {
const [contentFilter, setContentFilter] = React.useState('all');

const filteredSubmissions = React.useMemo(() => {
if (contentFilter === 'all') return communityContentData.submissions;
if (contentFilter === 'hacktoberfest') {
return communityContentData.submissions.filter(content =>
content.hacktoberfest || content.tags?.includes('hacktoberfest')
);
}
return communityContentData.submissions.filter(content => content.type === contentFilter);
}, [contentFilter]);

return (
<section className="w-full flex flex-col items-center gap-8 my-8">
<div className="text-center">
<Heading as="h1">{communityContentData.title}</Heading>
<p>{communityContentData.description}</p>
</div>

{/* Filter Tabs */}
<div className="flex justify-center gap-2 flex-wrap">
{[
{ id: 'all', label: 'All Content' },
{ id: 'hacktoberfest', label: '🎃 Hacktoberfest 2025' },
{ id: 'blog', label: '📝 Blog Posts' },
{ id: 'video', label: '🎥 Videos' }
].map((filter) => (
<button
key={filter.id}
className="button button--secondary"
onClick={() => setContentFilter(filter.id)}
style={contentFilter === filter.id ? {
backgroundColor: 'var(--ifm-color-primary)',
color: 'white',
border: '2px solid var(--ifm-color-primary-dark)'
} : {}}
>
{filter.label}
</button>
))}
</div>

{/* Content Grid */}
<div className="w-full max-w-6xl">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{/* Persistent Hacktoberfest CTA Card */}
<HacktoberfestCTACard />

{filteredSubmissions.map((content, index) => (
<ContentCard key={index} content={content} />
))}
</div>

{filteredSubmissions.length === 0 && (
<div className="text-center py-8">
<p className="text-textSubtle">No content found for this filter.</p>
</div>
)}
</div>
</section>
);
}

function HacktoberfestCTACard(): ReactNode {
return (
<div className="card h-full transition-all duration-200 hover:shadow-lg hover:-translate-y-1 bg-gradient-to-br from-orange-100 to-purple-100 border-2 border-orange-300">
{/* Thumbnail placeholder */}
<div className="card__image relative">
<div className="w-full h-48 bg-gradient-to-br from-orange-200 to-purple-200 flex items-center justify-center">
<span className="text-6xl">🎃</span>
</div>
<div className="absolute top-2 left-2 bg-orange-500 text-white px-2 py-1 rounded-full text-xs font-bold flex items-center gap-1">
🎃 Hacktoberfest
</div>
</div>

{/* Content */}
<div className="card__body">
{/* CTA Button as Title */}
<div className="mb-3">
<Link
href={communityContentData.submissionUrl}
className="button button--primary button--block button--lg"
target="_blank"
rel="noopener noreferrer"
>
🚀 Submit Your Content!
</Link>
</div>

{/* Description */}
<div className="text-sm text-textSubtle mb-2">
<p>Share your goose blog posts or videos with the community.</p>
</div>

<p className="text-xs text-textSubtle text-center">
Must be hosted on your own website
</p>
</div>
</div>
);
}

function ContentCard({ content }): ReactNode {
const getTypeIcon = (type: string) => {
switch (type) {
case 'blog': return '📝';
case 'video': return '🎥';
case 'tutorial': return '📚';
case 'case-study': return '📊';
default: return '📄';
}
};

const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric'
});
};

return (
<div className="card h-full transition-all duration-200 hover:shadow-lg hover:-translate-y-1">
{/* Thumbnail */}
<div className="card__image relative">
<img
src={content.thumbnail || 'https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=400&h=225&fit=crop&crop=entropy&auto=format'}
alt={content.title}
className="w-full h-48 object-cover"
loading="lazy"
/>
</div>

{/* Content */}
<div className="card__body">
<div className="flex items-start gap-2 mb-2">
<span className="text-lg">{getTypeIcon(content.type)}</span>
<h3 className="text-lg font-semibold line-clamp-2 flex-1">
<Link href={content.url} className="text-inherit hover:text-primary">
{content.title}
</Link>
</h3>
</div>

{/* Author and Date */}
<div className="flex items-center justify-between text-sm text-textSubtle mb-3">
<div className="flex items-center gap-2">
<img
src={`https://github.com/${content.author.handle}.png`}
alt={content.author.name}
className="w-6 h-6 rounded-full"
/>
<Link href={`https://github.com/${content.author.handle}`} className="hover:text-primary">
@{content.author.handle}
</Link>
</div>
<span>📅 {formatDate(content.submittedDate)}</span>
</div>


</div>


</div>
);
}

export function StarsCard({contributor}): ReactNode {
return (
<div className={`col ${contributor.totalCount <= 3 ? 'col--4' : 'col--2'} mb-8`}>
Expand Down Expand Up @@ -260,11 +429,12 @@ 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."
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 />
<CommunityContentSpotlightSection />
</main>
</Layout>
);
Expand Down