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
62 changes: 59 additions & 3 deletions src/data/contributors-manual.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
{
"generated_at": "2025-09-03T13:46:04.752Z",
"total_contributors": 1,
"total_contributors": 3,
"total_contributions": 24,
"total_impact_score": 24,
"total_recent_activity": 24,
"scoring_method": "manual_entry",
"contributors": [
{
"id": 999999999,
"id": 999999999,
"login": "Robobc",
"name": "Roberto Catalano",
"avatar_url": "https://avatars.githubusercontent.com/u/999999999?v=4",
"html_url": "https://github.com/Robobc",
"bio": "Solutions Architect at AWS in Zurich",
"bio": "Created some great example repositories and helped with the documentation.",
"company": "Amazon Web Services",
"location": "Zürich",
"blog": null,
Expand All @@ -33,6 +33,62 @@
"total_reviews": 0,
"last_activity": "2025-09-03T13:46:04Z",
"pr_success_rate": 0
},
{
"id": 0,
"login": "gwoodwa1",
"name": "Gary Woodward",
"avatar_url": "https://github.com/gwoodwa1.png",
"html_url": "https://github.com/gwoodwa1",
"bio": "Supported Go-port by giving gnmi support over grpc transport",
"company": null,
"location": null,
"blog": null,
"hireable": null,
"public_repos": 0,
"followers": 0,
"following": 0,
"created_at": null,
"contributions": 0,
"repositories": [
"utcp-go"
],
"repo_count": 1,
"impact_score": 0,
"total_prs": 0,
"total_merged_prs": 0,
"total_recent_commits": 0,
"total_reviews": 0,
"last_activity": null,
"pr_success_rate": 0
},
{
"id": 0,
"login": "armanzeroeight",
"name": "Arman Nourifar",
"avatar_url": "https://github.com/armanzeroeight.png",
"html_url": "https://github.com/armanzeroeight",
"bio": "For social media content and his excellent gifs",
"company": null,
"location": "Oslo, Norway",
"blog": "https://medium.com/@arman08",
"hireable": null,
"public_repos": 0,
"followers": 0,
"following": 0,
"created_at": null,
"contributions": 0,
"repositories": [
"community"
],
"repo_count": 1,
"impact_score": 0,
"total_prs": 0,
"total_merged_prs": 0,
"total_recent_commits": 0,
"total_reviews": 0,
"last_activity": null,
"pr_success_rate": 0
}
]
}
84 changes: 84 additions & 0 deletions src/pages/hall-of-fame.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -843,4 +843,88 @@
.linesChangedLabel {
font-size: 0.4rem;
}
}

/* Other Contributors (non-coder) section */
.otherContributorsSection {
padding: 3rem 1rem 0;
max-width: 1200px;
margin: 0 auto;
}

.otherContributorsHeader {
text-align: center;
margin-bottom: 1.5rem;
}

.otherContributorsTitle {
font-size: 2rem;
font-weight: 700;
margin: 0 0 0.5rem 0;
background: linear-gradient(135deg, #ffffff 0%, #8b5cf6 100%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}

.otherContributorsSubtitle {
font-size: 1rem;
color: #888888;
margin: 0;
}

.otherContributorsGrid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 1rem;
}

.otherContributorCard {
background: linear-gradient(145deg, #111111 0%, #0a0a0a 100%);
border: 1px solid #222222;
border-radius: 12px;
padding: 1rem;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
}

.otherContributorIcon {
width: 72px;
height: 72px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
background: linear-gradient(135deg, #1a1a1a 0%, #2a2a2a 100%);
border: 1px solid #333333;
}

.otherContributorImage {
width: 100%;
height: 100%;
border-radius: 50%;
object-fit: cover;
}

.otherContributorRole {
font-size: 1.05rem;
font-weight: 600;
margin: 0.25rem 0 0;
color: #ffffff;
}

.otherContributorThanks {
font-size: 0.9rem;
color: #cccccc;
margin: 0;
text-align: center;
}

@media (max-width: 768px) {
.otherContributorsSection {
padding-top: 2rem;
}
}
66 changes: 66 additions & 0 deletions src/pages/hall-of-fame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,60 @@ const ContributorRow = ({ contributor, rank }: { contributor: DisplayContributor

export default function Contributors(): React.ReactNode {
const contributors = useContributors();
const [otherContributors, setOtherContributors] = useState<Array<{ avatar?: string; role: string; thanks: string; username: string }>>([]);

useEffect(() => {
const loadOtherContributors = async () => {
try {
const manualModule = await import('../data/contributors-manual.json');
const manualData = manualModule.default as { contributors?: Array<any> };
const mapped = (manualData?.contributors || []).map((c: any) => {
const username: string = c?.login || c?.name || 'friend';
const name: string = c?.name || username;
const bio: string | null = c?.bio || null;
const avatar: string | undefined = c?.avatar_url || undefined;
return {
avatar,
role: name,
thanks: bio || 'Thank you for supporting UTCP.',
username
};
});
setOtherContributors(mapped);
} catch (err) {
// Fallback to a small static list when manual file is unavailable
setOtherContributors([
{ avatar: undefined, role: 'Maintainer', thanks: 'Guiding the project and ensuring high quality.', username: 'maintainer' },
{ avatar: undefined, role: 'Designer', thanks: 'Improving UX and visual language.', username: 'designer' },
{ avatar: undefined, role: 'Researcher', thanks: 'Exploring the landscape and informing decisions.', username: 'researcher' }
]);
}
};
loadOtherContributors();
}, []);

const OtherContributorCard = ({ avatar, role, thanks, username }: { avatar?: string; role: string; thanks: string; username: string }) => {
const [imageError, setImageError] = useState(false);
const showImage = Boolean(avatar) && !imageError;
return (
<div className={styles.otherContributorCard}>
<div className={styles.otherContributorIcon}>
{showImage ? (
<img
src={avatar as string}
alt={`${role} avatar`}
className={styles.otherContributorImage}
onError={() => setImageError(true)}
/>
) : (
<span className={styles.avatarEmoji}>{generateFallbackAvatar(username)}</span>
)}
</div>
<div className={styles.otherContributorRole}>{role}</div>
<div className={styles.otherContributorThanks}>{thanks}</div>
</div>
);
};

return (
<Layout
Expand Down Expand Up @@ -502,6 +556,18 @@ export default function Contributors(): React.ReactNode {
/>
))}
</div>

<div className={styles.otherContributorsSection}>
<div className={styles.otherContributorsHeader}>
<h2 className={styles.otherContributorsTitle}>Special Thanks</h2>
<p className={styles.otherContributorsSubtitle}>Beyond code, here are some of our champions</p>
</div>
<div className={styles.otherContributorsGrid}>
{otherContributors.map((oc, i) => (
<OtherContributorCard key={i} avatar={oc.avatar} role={oc.role} thanks={oc.thanks} username={oc.username} />
))}
</div>
</div>

{contributors.length > 1 && contributors[0]?.total_changes > 0 && (
<div className={styles.statsFooter}>
Expand Down
Loading