Skip to content

Commit 6d536b2

Browse files
author
yashksaini-coder
committed
feat(app): 126 Clickable cards, with real-time stats of RecodeHive Org
Signed-off-by: yashksaini-coder <[email protected]>
1 parent 5a1e5e9 commit 6d536b2

File tree

5 files changed

+568
-87
lines changed

5 files changed

+568
-87
lines changed

src/components/Community/LandingCommunity.css

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
max-width: 100%;
1111
margin-bottom: 1rem;
1212
padding: auto 1rem;
13+
flex-wrap: wrap;
1314
}
1415

1516
.landing-community .landing-community__header .landing-community__title {
@@ -26,6 +27,12 @@
2627
text-shadow: 0 0 1px var(--ifm-color-primary);
2728
}
2829

30+
.landing-community .landing-community__header .landing-community__error {
31+
color: var(--ifm-color-warning);
32+
font-size: 0.9rem;
33+
margin-top: 0.5rem;
34+
}
35+
2936
.landing-community .landing-community__content {
3037
display: grid;
3138
grid-template-columns: 1fr 1fr;
@@ -48,6 +55,23 @@
4855
border-radius: 1rem;
4956
background-color: var(--ifm-color-background);
5057
box-shadow: 0 0 1px var(--ifm-color-primary);
58+
transition: all 0.3s ease;
59+
position: relative;
60+
}
61+
62+
.landing-community .landing-community__content .landing-community__stats .landing-community__stat-item.clickable {
63+
cursor: pointer;
64+
}
65+
66+
.landing-community .landing-community__content .landing-community__stats .landing-community__stat-item.clickable:hover,
67+
.landing-community .landing-community__content .landing-community__stats .landing-community__stat-item.clickable:focus {
68+
transform: scale(1.02);
69+
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
70+
outline: none;
71+
}
72+
73+
.landing-community .landing-community__content .landing-community__stats .landing-community__stat-item.loading {
74+
opacity: 0.7;
5175
}
5276

5377
.landing-community .landing-community__content .landing-community__stats .landing-community__stat-item:hover {
@@ -62,6 +86,34 @@
6286
color: var(--ifm-color-primary);
6387
text-shadow: 0 0 1px var(--ifm-color-primary);
6488
padding-bottom: 0.5rem;
89+
position: relative;
90+
}
91+
92+
.landing-community .landing-community__content .landing-community__stats .landing-community__stat-item .landing-community__loading {
93+
display: flex;
94+
align-items: center;
95+
justify-content: center;
96+
font-size: 2rem;
97+
}
98+
99+
.landing-community .loading-spinner {
100+
animation: spin 2s linear infinite;
101+
}
102+
103+
@keyframes spin {
104+
0% { transform: rotate(0deg); }
105+
100% { transform: rotate(360deg); }
106+
}
107+
108+
.landing-community .external-link-icon {
109+
font-size: 0.8em;
110+
margin-left: 0.3rem;
111+
opacity: 0.7;
112+
transition: opacity 0.2s ease;
113+
}
114+
115+
.landing-community .landing-community__content .landing-community__stats .landing-community__stat-item.clickable:hover .external-link-icon {
116+
opacity: 1;
65117
}
66118

67119
.landing-community .landing-community__content .landing-community__stats .landing-community__stat-item .landing-community__stat-description {
@@ -75,12 +127,19 @@
75127
border-radius: 1rem;
76128
background-color: var(--ifm-color-background);
77129
box-shadow: 0 0 1px var(--ifm-color-primary);
130+
transition: all 0.3s ease;
131+
position: relative;
78132
}
79133

80-
.landing-community .landing-community__content .landing-community__info:hover {
134+
.landing-community .landing-community__content .landing-community__info.clickable {
81135
cursor: pointer;
82-
transform: scale(1.01);
83-
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.4);
136+
}
137+
138+
.landing-community .landing-community__content .landing-community__info.clickable:hover,
139+
.landing-community .landing-community__content .landing-community__info.clickable:focus {
140+
transform: scale(1.01);
141+
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
142+
outline: none;
84143
}
85144

86145
.landing-community .landing-community__content .landing-community__info .landing-community__image {
@@ -100,12 +159,27 @@
100159
color: var(--ifm-color-primary);
101160
text-shadow: 0 0 1px var(--ifm-color-primary);
102161
text-decoration: none;
162+
font-weight: 600;
103163
}
104164

105165
.landing-community .landing-community__content .landing-community__info .landing-community__info-text .landing-community__link:hover {
106166
text-decoration: underline;
107167
}
108168

169+
.landing-community .external-link-indicator {
170+
display: flex;
171+
align-items: center;
172+
justify-content: center;
173+
margin-top: 0.5rem;
174+
gap: 0.5rem;
175+
opacity: 0.7;
176+
transition: opacity 0.2s ease;
177+
}
178+
179+
.landing-community .landing-community__content .landing-community__info.clickable:hover .external-link-indicator {
180+
opacity: 1;
181+
}
182+
109183
@media screen and (max-width: 768px) {
110184
.landing-community .landing-community__content {
111185
grid-template-columns: 1fr;

src/components/Community/index.tsx

Lines changed: 93 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,15 @@ type Props = {
77
};
88

99
export const LandingCommunity: FC<Props> = ({ className }) => {
10-
const { githubStarCountText, githubContributorsCount, githubForksCount } = useCommunityStatsContext();
10+
const {
11+
githubStarCountText,
12+
githubContributorsCountText,
13+
githubForksCountText,
14+
githubReposCountText,
15+
loading,
16+
error
17+
} = useCommunityStatsContext();
18+
1119
const [state, setState] = useState({
1220
stat0: 0,
1321
stat1: 0,
@@ -18,28 +26,29 @@ export const LandingCommunity: FC<Props> = ({ className }) => {
1826
const generateList = useMemo(() => [
1927
{
2028
stat: githubStarCountText,
21-
description: "Stars on our GitHub repository, showcase the support and contribution, we recieved from the community.",
29+
description: "Stars across all our GitHub repositories, showcasing the support and appreciation from the community.",
2230
href: "https://github.com/recodehive",
23-
// https://github.com/CodeHarborHub/codeharborhub.github.io/stargazers
31+
label: "GitHub Stars"
2432
},
2533
{
26-
stat: 20,
27-
description: "Live projects on recodehive, demonstrating the power of open-source collaboration.",
34+
stat: githubReposCountText,
35+
description: "Live public projects on RecodHive, demonstrating the power of open-source collaboration.",
36+
href: "https://github.com/orgs/recodehive/repositories?q=visibility%3Apublic+archived%3Afalse",
37+
label: "Public Repositories"
2838
},
2939
{
30-
stat: githubContributorsCount,
31-
description: "List of Contributors who have made our repository better.",
32-
href: "https://github.com/recodehive",
33-
// https://github.com/CodeHarborHub/codeharborhub.github.io/graphs/contributors
40+
stat: githubContributorsCountText,
41+
description: "Amazing contributors who have made our repositories better and helped build our community.",
42+
href: "https://github.com/orgs/recodehive/people",
43+
label: "Contributors"
3444
},
3545
{
36-
stat: githubForksCount,
37-
description: "Forks of our repository, showing how our community extends our work.",
38-
href: "https://github.com/recodehive",
39-
40-
//https://github.com/CodeHarborHub/codeharborhub.github.io/network/members
46+
stat: githubForksCountText,
47+
description: "Forks of our repositories, showing how our community extends and builds upon our work.",
48+
href: "https://github.com/orgs/recodehive/discussions",
49+
label: "Community Forks"
4150
},
42-
], [githubStarCountText, githubContributorsCount, githubForksCount]);
51+
], [githubStarCountText, githubReposCountText, githubContributorsCountText, githubForksCountText]);
4352

4453
const handleDynamicChange = (target: number, index: number) => {
4554
let count = 0;
@@ -54,11 +63,22 @@ export const LandingCommunity: FC<Props> = ({ className }) => {
5463
}, 20);
5564
};
5665

66+
const handleCardClick = (href: string) => {
67+
if (href) {
68+
window.open(href, '_blank', 'noopener,noreferrer');
69+
}
70+
};
71+
5772
useEffect(() => {
58-
generateList.forEach((item, index) => {
59-
handleDynamicChange(Number(item.stat), index);
60-
});
61-
}, [generateList]);
73+
if (!loading) {
74+
generateList.forEach((item, index) => {
75+
const numericStat = typeof item.stat === 'string' ?
76+
parseInt(item.stat.replace(/[^\d]/g, '')) || 0 :
77+
Number(item.stat);
78+
handleDynamicChange(numericStat, index);
79+
});
80+
}
81+
}, [generateList, loading]);
6282

6383
return (
6484
<div className={`landing-community ${className || ""}`}>
@@ -67,61 +87,83 @@ export const LandingCommunity: FC<Props> = ({ className }) => {
6787
Discover the strength of our{" "}
6888
<span className="landing-community__highlight">amazing community</span>.
6989
</h2>
90+
{error && (
91+
<div className="landing-community__error">
92+
<small>⚠️ Stats may be cached or incomplete</small>
93+
</div>
94+
)}
7095
</div>
7196

7297
<div className="landing-community__content">
7398
<div className="landing-community__stats">
7499
{generateList.map((item, index) => (
75-
<span key={index} className="landing-community__stat-item">
100+
<div
101+
key={index}
102+
className={`landing-community__stat-item ${item.href ? 'clickable' : ''} ${loading ? 'loading' : ''}`}
103+
onClick={() => handleCardClick(item.href)}
104+
role={item.href ? "button" : "presentation"}
105+
tabIndex={item.href ? 0 : -1}
106+
onKeyDown={(e) => {
107+
if (item.href && (e.key === 'Enter' || e.key === ' ')) {
108+
e.preventDefault();
109+
handleCardClick(item.href);
110+
}
111+
}}
112+
title={item.href ? `Click to visit ${item.label}` : item.label}
113+
>
76114
<div className="landing-community__stat-value">
77-
{item.href ? (
78-
<a
79-
href={item.href}
80-
target="_blank"
81-
rel="noopener noreferrer"
82-
>
83-
{`${state[`stat${index}`]}${index !== 1 ? "" : ""}`}
84-
</a>
115+
{loading ? (
116+
<div className="landing-community__loading">
117+
<span className="loading-spinner"></span>
118+
</div>
85119
) : (
86-
`${state[`stat${index}`]}`
120+
<span>
121+
{item.stat}
122+
{item.href && <span className="external-link-icon"></span>}
123+
</span>
87124
)}
88125
</div>
89126
<div className="landing-community__stat-description">
90127
{item.description}
91128
</div>
92-
</span>
129+
</div>
93130
))}
94131
</div>
95132

96-
<div className="landing-community__info">
133+
<div
134+
className="landing-community__info clickable"
135+
onClick={() => handleCardClick("https://github.com/recodehive")}
136+
role="button"
137+
tabIndex={0}
138+
onKeyDown={(e) => {
139+
if (e.key === 'Enter' || e.key === ' ') {
140+
e.preventDefault();
141+
handleCardClick("https://github.com/recodehive");
142+
}
143+
}}
144+
title="Click to visit RecodHive GitHub Organization"
145+
>
97146
<img
98147
className="landing-community__image"
99148
src="/community.png"
100149
alt="team collaboration"
101150
loading="lazy"
102151
/>
103152
<div className="landing-community__info-text">
104-
Our developers are the core of Hive community. We take pride in
153+
Our developers are the core of RecodHive community. We take pride in
105154
our{" "}
106-
<a
107-
href="https://github.com/recodehive"
108-
//https://github.com/CodeHarborHub/codeharborhub.github.io/graphs/contributors
109-
target="_blank"
110-
rel="noopener noreferrer"
111-
className="landing-community__link"
112-
>
113-
GitHub community
114-
</a>{" "}
115-
with over{" "}
116-
<a
117-
href="https://github.com/recodehive"
118-
target="_blank"
119-
rel="noopener noreferrer"
120-
className="landing-community__link"
121-
>
122-
500+ contributors
123-
</a>{" "}
124-
powering recodehive.
155+
<span className="landing-community__link">
156+
GitHub organization
157+
</span>{" "}
158+
with amazing{" "}
159+
<span className="landing-community__link">
160+
contributors and maintainers
161+
</span>{" "}
162+
powering RecodHive's growth.
163+
<div className="external-link-indicator">
164+
<span className="external-link-icon"></span>
165+
<small>Click to explore our GitHub</small>
166+
</div>
125167
</div>
126168
</div>
127169
</div>

0 commit comments

Comments
 (0)