From 80dd966cdf3162d99764ceda67d7722986a91613 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 06:45:49 +0000 Subject: [PATCH 1/2] chore: refactor BusinessCard component Co-authored-by: is0692vs <135803462+is0692vs@users.noreply.github.com> --- src/components/BusinessCard.tsx | 281 +----------------- .../BusinessCardBlocks/AvatarBlock.tsx | 19 ++ .../BusinessCardBlocks/BioBlock.tsx | 64 ++++ .../BusinessCardBlocks/StatsBlock.tsx | 74 +++++ .../BusinessCardBlocks/TopLanguagesBlock.tsx | 93 ++++++ .../BusinessCardBlocks/TopReposBlock.tsx | 44 +++ src/components/BusinessCardBlocks/index.ts | 5 + 7 files changed, 313 insertions(+), 267 deletions(-) create mode 100644 src/components/BusinessCardBlocks/AvatarBlock.tsx create mode 100644 src/components/BusinessCardBlocks/BioBlock.tsx create mode 100644 src/components/BusinessCardBlocks/StatsBlock.tsx create mode 100644 src/components/BusinessCardBlocks/TopLanguagesBlock.tsx create mode 100644 src/components/BusinessCardBlocks/TopReposBlock.tsx create mode 100644 src/components/BusinessCardBlocks/index.ts diff --git a/src/components/BusinessCard.tsx b/src/components/BusinessCard.tsx index 47820e12..d6903095 100644 --- a/src/components/BusinessCard.tsx +++ b/src/components/BusinessCard.tsx @@ -1,13 +1,7 @@ import { forwardRef } from "react"; import type { CardBlockId, CardDisplayOptions, CardLayout, UserSummary } from "@/lib/types"; import { DEFAULT_CARD_LAYOUT } from "@/lib/types"; -import { - BuildingIcon, - CalendarIcon, - LinkIcon, - MapPinIcon, - TwitterIcon, -} from "./Icons"; +import { AvatarBlock, BioBlock, StatsBlock, TopLanguagesBlock, TopReposBlock } from "./BusinessCardBlocks"; type Props = { summary: UserSummary; @@ -21,18 +15,6 @@ const BusinessCard = forwardRef(({ summary, layout, optio if (!profile) return null; const activeLayout = layout ?? DEFAULT_CARD_LAYOUT; - const { - showCompany = false, - showLocation = false, - showWebsite = false, - showTwitter = false, - showJoinedDate = false, - showTopics = false, - showContributionBreakdown = false, - showStreaks = false, - showInterests = false, - showActivityBreakdown = false, - } = options || {}; const topLanguages = repositories?.languages.slice(0, 5) || []; const topTopics = repositories?.topics.slice(0, 10) || []; @@ -41,263 +23,28 @@ const BusinessCard = forwardRef(({ summary, layout, optio ? profile.pinnedRepos.slice(0, 2) : repositories?.topRepos.slice(0, 2) || []; - const renderAvatarBlock = () => ( -
- {/* eslint-disable-next-line @next/next/no-img-element */} - {profile.login} -
-

- {profile.name || profile.login} -

-

@{profile.login}

-
-
- ); - - const renderBioBlock = () => ( - <> -
-

- {profile.bio || "No bio available."} -

-
- - {(showCompany || showLocation || showWebsite || showTwitter || showJoinedDate) && ( -
- {showCompany && profile.company && ( -
- - {profile.company} -
- )} - {showLocation && profile.location && ( -
- - {profile.location} -
- )} - {showWebsite && profile.blog && ( -
- - - {profile.blog.replace(/^https?:\/\//, "")} - -
- )} - {showTwitter && profile.twitter_username && ( -
- - @{profile.twitter_username} -
- )} - {showJoinedDate && ( -
- - - Joined {new Date(profile.created_at).toLocaleDateString("en-US", { month: "short", year: "numeric" })} - -
- )} -
- )} - - ); - - const renderStatsBlock = () => ( - <> -
-
-
- {(contributions?.totalContributions ?? 0).toLocaleString()} -
-
Contributions
-
-
-
- {profile.followers.toLocaleString()} -
-
Followers
-
-
-
- {profile.public_repos.toLocaleString()} -
-
Repositories
-
-
- - {showContributionBreakdown && contributions && ( -
-
- Commits - {contributions.totalCommits.toLocaleString()} -
-
- Pull Requests - {contributions.totalPRs.toLocaleString()} -
-
- Issues - {contributions.totalIssues.toLocaleString()} -
-
- Code Reviews - {contributions.totalReviews.toLocaleString()} -
-
- )} - - {showStreaks && contributions && ( -
-
-
- {contributions.longestStreak} days -
-
Longest Streak
-
-
-
- {contributions.currentStreak} days -
-
Current Streak
-
-
- )} - - ); - - const renderTopLanguagesBlock = () => ( -
- {topLanguages.length > 0 && ( -
-

- Top Languages -

-
- {topLanguages.map((lang) => ( -
- - {lang.name} - {lang.percentage.toFixed(1)}% -
- ))} -
-
- )} - - {showTopics && topTopics.length > 0 && ( -
-

- Top Topics -

-
- {topTopics.map((topic) => ( - - #{topic.name} - - ))} -
-
- )} - - {showInterests && interests && interests.topTopics.length > 0 && ( -
-

- Interests -

-
- {interests.topTopics.slice(0, 8).map((topic) => ( - - #{topic.name} - - ))} -
-
- )} - - {showActivityBreakdown && activity && activity.eventBreakdown.length > 0 && ( -
-

- Recent Activity -

-
- {activity.eventBreakdown.slice(0, 5).map((event) => ( -
- {event.type} - {event.count} -
- ))} -
-
- )} -
- ); - - const renderTopReposBlock = () => ( -
- {reposToShow.length > 0 && ( - <> -

- Top Repositories -

-
- {reposToShow.map((repo) => ( -
-
{repo.name}
-
- {repo.primaryLanguage && ( - - - {repo.primaryLanguage.name} - - )} - - - {repo.stargazerCount.toLocaleString()} - -
-
- ))} -
- - )} -
- ); - const renderBlock = (blockId: CardBlockId) => { if (blockId === "avatar") { - return renderAvatarBlock(); + return ; } if (blockId === "bio") { - return renderBioBlock(); + return ; } if (blockId === "stats") { - return renderStatsBlock(); + return ; } if (blockId === "topLanguages") { - return renderTopLanguagesBlock(); + return ( + + ); } - return renderTopReposBlock(); + return ; }; const fullBlocks = activeLayout.blocks.filter((block) => block.column === "full" && block.visible); diff --git a/src/components/BusinessCardBlocks/AvatarBlock.tsx b/src/components/BusinessCardBlocks/AvatarBlock.tsx new file mode 100644 index 00000000..6af55d33 --- /dev/null +++ b/src/components/BusinessCardBlocks/AvatarBlock.tsx @@ -0,0 +1,19 @@ +import type { UserProfile } from "@/lib/types"; + +export const AvatarBlock = ({ profile }: { profile: UserProfile }) => ( +
+ {/* eslint-disable-next-line @next/next/no-img-element */} + {profile.login} +
+

+ {profile.name || profile.login} +

+
@{profile.login}
+
+
+); diff --git a/src/components/BusinessCardBlocks/BioBlock.tsx b/src/components/BusinessCardBlocks/BioBlock.tsx new file mode 100644 index 00000000..71ca7d22 --- /dev/null +++ b/src/components/BusinessCardBlocks/BioBlock.tsx @@ -0,0 +1,64 @@ +import type { UserProfile, CardDisplayOptions } from "@/lib/types"; +import { BuildingIcon, CalendarIcon, LinkIcon, MapPinIcon, TwitterIcon } from "../Icons"; + +type Props = { + profile: UserProfile; + options?: CardDisplayOptions; +}; + +export const BioBlock = ({ profile, options }: Props) => { + const { + showCompany = false, + showLocation = false, + showWebsite = false, + showTwitter = false, + showJoinedDate = false, + } = options || {}; + + return ( + <> +

+ {profile.bio || "No bio available."} +

+ + {(showCompany || showLocation || showWebsite || showTwitter || showJoinedDate) && ( +
+ {showCompany && profile.company && ( +
+ + {profile.company} +
+ )} + {showLocation && profile.location && ( +
+ + {profile.location} +
+ )} + {showWebsite && profile.blog && ( +
+ + + {profile.blog.replace(/^https?:\/\//, "")} + +
+ )} + {showTwitter && profile.twitter_username && ( +
+ + @{profile.twitter_username} +
+ )} + {showJoinedDate && ( +
+ + + Joined {new Date(profile.created_at).toLocaleDateString("en-US", { month: "short", year: "numeric" })} + +
+ )} +
+ )} + + ); +}; diff --git a/src/components/BusinessCardBlocks/StatsBlock.tsx b/src/components/BusinessCardBlocks/StatsBlock.tsx new file mode 100644 index 00000000..099f0890 --- /dev/null +++ b/src/components/BusinessCardBlocks/StatsBlock.tsx @@ -0,0 +1,74 @@ +import type { UserProfile, GithubContributions, CardDisplayOptions } from "@/lib/types"; + +type Props = { + profile: UserProfile; + contributions: GithubContributions | null; + options?: CardDisplayOptions; +}; + +export const StatsBlock = ({ profile, contributions, options }: Props) => { + const { showContributionBreakdown = false, showStreaks = false } = options || {}; + + return ( + <> +
+
+
+ {(contributions?.totalContributions ?? 0).toLocaleString()} +
+
Contributions
+
+
+
+ {profile.followers.toLocaleString()} +
+
Followers
+
+
+
+ {profile.public_repos.toLocaleString()} +
+
Repositories
+
+
+ + {showContributionBreakdown && contributions && ( +
+
+ Commits + {contributions.totalCommits.toLocaleString()} +
+
+ Pull Requests + {contributions.totalPRs.toLocaleString()} +
+
+ Issues + {contributions.totalIssues.toLocaleString()} +
+
+ Code Reviews + {contributions.totalReviews.toLocaleString()} +
+
+ )} + + {showStreaks && contributions && ( +
+
+
+ {contributions.longestStreak} days +
+
Longest Streak
+
+
+
+ {contributions.currentStreak} days +
+
Current Streak
+
+
+ )} + + ); +}; diff --git a/src/components/BusinessCardBlocks/TopLanguagesBlock.tsx b/src/components/BusinessCardBlocks/TopLanguagesBlock.tsx new file mode 100644 index 00000000..3f56e810 --- /dev/null +++ b/src/components/BusinessCardBlocks/TopLanguagesBlock.tsx @@ -0,0 +1,93 @@ +import type { CardDisplayOptions, TopLanguage, Topic, UserInterests, GithubActivity } from "@/lib/types"; + +type Props = { + topLanguages: TopLanguage[]; + topTopics: Topic[]; + interests: UserInterests | null; + activity: GithubActivity | null; + options?: CardDisplayOptions; +}; + +export const TopLanguagesBlock = ({ topLanguages, topTopics, interests, activity, options }: Props) => { + const { + showTopics = false, + showInterests = false, + showActivityBreakdown = false, + } = options || {}; + + return ( +
+ {topLanguages.length > 0 && ( +
+

+ Top Languages +

+
+ {topLanguages.map((lang) => ( +
+ + {lang.name} + {lang.percentage.toFixed(1)}% +
+ ))} +
+
+ )} + + {showTopics && topTopics.length > 0 && ( +
+

+ Top Topics +

+
+ {topTopics.map((topic) => ( + + #{topic.name} + + ))} +
+
+ )} + + {showInterests && interests && interests.topTopics.length > 0 && ( +
+

+ Interests +

+
+ {interests.topTopics.slice(0, 8).map((topic) => ( + + #{topic.name} + + ))} +
+
+ )} + + {showActivityBreakdown && activity && activity.eventBreakdown.length > 0 && ( +
+

+ Recent Activity +

+
+ {activity.eventBreakdown.slice(0, 5).map((event) => ( +
+ {event.type} + {event.count} +
+ ))} +
+
+ )} +
+ ); +}; diff --git a/src/components/BusinessCardBlocks/TopReposBlock.tsx b/src/components/BusinessCardBlocks/TopReposBlock.tsx new file mode 100644 index 00000000..ef38efde --- /dev/null +++ b/src/components/BusinessCardBlocks/TopReposBlock.tsx @@ -0,0 +1,44 @@ +import type { Repository } from "@/lib/types"; + +type Props = { + reposToShow: Repository[]; +}; + +export const TopReposBlock = ({ reposToShow }: Props) => { + return ( +
+ {reposToShow.length > 0 && ( + <> +

+ Top Repositories +

+
+ {reposToShow.map((repo) => ( +
+
{repo.name}
+
+ {repo.primaryLanguage && ( + + + {repo.primaryLanguage.name} + + )} + + + {repo.stargazerCount.toLocaleString()} + +
+
+ ))} +
+ + )} +
+ ); +}; diff --git a/src/components/BusinessCardBlocks/index.ts b/src/components/BusinessCardBlocks/index.ts new file mode 100644 index 00000000..29d7b2ba --- /dev/null +++ b/src/components/BusinessCardBlocks/index.ts @@ -0,0 +1,5 @@ +export { AvatarBlock } from './AvatarBlock'; +export { BioBlock } from './BioBlock'; +export { StatsBlock } from './StatsBlock'; +export { TopLanguagesBlock } from './TopLanguagesBlock'; +export { TopReposBlock } from './TopReposBlock'; From 3169bfa3ce64a63666b3cf77f8c0979a68909774 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 06:51:59 +0000 Subject: [PATCH 2/2] fix: resolve type import errors in BusinessCard blocks Co-authored-by: is0692vs <135803462+is0692vs@users.noreply.github.com> --- src/components/BusinessCardBlocks/StatsBlock.tsx | 4 ++-- .../BusinessCardBlocks/TopLanguagesBlock.tsx | 10 ++++++---- src/components/BusinessCardBlocks/TopReposBlock.tsx | 4 +++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/components/BusinessCardBlocks/StatsBlock.tsx b/src/components/BusinessCardBlocks/StatsBlock.tsx index 099f0890..746d7905 100644 --- a/src/components/BusinessCardBlocks/StatsBlock.tsx +++ b/src/components/BusinessCardBlocks/StatsBlock.tsx @@ -1,8 +1,8 @@ -import type { UserProfile, GithubContributions, CardDisplayOptions } from "@/lib/types"; +import type { UserProfile, ContributionData, CardDisplayOptions } from "@/lib/types"; type Props = { profile: UserProfile; - contributions: GithubContributions | null; + contributions: ContributionData | null; options?: CardDisplayOptions; }; diff --git a/src/components/BusinessCardBlocks/TopLanguagesBlock.tsx b/src/components/BusinessCardBlocks/TopLanguagesBlock.tsx index 3f56e810..4558cebb 100644 --- a/src/components/BusinessCardBlocks/TopLanguagesBlock.tsx +++ b/src/components/BusinessCardBlocks/TopLanguagesBlock.tsx @@ -1,10 +1,12 @@ -import type { CardDisplayOptions, TopLanguage, Topic, UserInterests, GithubActivity } from "@/lib/types"; +import type { CardDisplayOptions, LanguageStats, InterestsData, ActivityData } from "@/lib/types"; + +type Topic = { name: string; count: number }; type Props = { - topLanguages: TopLanguage[]; + topLanguages: LanguageStats[]; topTopics: Topic[]; - interests: UserInterests | null; - activity: GithubActivity | null; + interests: InterestsData | null; + activity: ActivityData | null; options?: CardDisplayOptions; }; diff --git a/src/components/BusinessCardBlocks/TopReposBlock.tsx b/src/components/BusinessCardBlocks/TopReposBlock.tsx index ef38efde..b7183a09 100644 --- a/src/components/BusinessCardBlocks/TopReposBlock.tsx +++ b/src/components/BusinessCardBlocks/TopReposBlock.tsx @@ -1,4 +1,6 @@ -import type { Repository } from "@/lib/types"; +import type { PinnedRepo, TopRepo } from "@/lib/types"; + +type Repository = PinnedRepo | TopRepo; type Props = { reposToShow: Repository[];