Skip to content
2 changes: 1 addition & 1 deletion src/components/Navbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default function Navbar() {
return (
<nav style={{
position: 'sticky', top: 0, zIndex: 100,
background: 'rgba(13,13,13,.97)',
background: 'var(--bg)',
backdropFilter: 'blur(10px)',
borderBottom: '1px solid var(--border)',
padding: '0 24px',
Expand Down
20 changes: 14 additions & 6 deletions src/context/AppContext.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createContext, useContext, useState, useCallback, useEffect } from 'react'
import { fetchOrg, fetchRepos, fetchContributors, fetchIssues, } from '../services/github'
import { buildAnalyticalModel } from '../services/analytics'
import { buildAnalyticalModel, getTopRepositories } from '../services/analytics'

const Ctx = createContext(null)

Expand Down Expand Up @@ -58,7 +58,7 @@ export function AppProvider({ children }) {

return () => clearTimeout(timeout)
}, [rateLimit])

const [totalRepo, setTotalRepo] = useState(0);
const savePat = useCallback(token => {
setPat(token)
token ? localStorage.setItem('oe_pat', token) : localStorage.removeItem('oe_pat')
Expand All @@ -80,12 +80,20 @@ export function AppProvider({ children }) {
reposPerOrg[org.login] = await fetchRepos(org.login, org.public_repos, pat)
}))

const total = Object.values(reposPerOrg).reduce(
(sum, repos) => sum + repos.length,
0
);

setTotalRepo(total);

setLoadMsg('Fetching contributor data for top repositories...')
const contribsPerRepo = {}
for (const org of validOrgs) {
const top = pat ? (reposPerOrg[org.login] || []) : (reposPerOrg[org.login] || [])
.sort((a, b) => b.stargazers_count - a.stargazers_count)
.slice(0, 10)

const top = pat ? (reposPerOrg[org.login] || []) : getTopRepositories(reposPerOrg[org.login] || [], 10);
reposPerOrg[org.login] = top; // Update to only include top repos

await Promise.allSettled(top.map(async repo => {
contribsPerRepo[`${org.login}/${repo.name}`] = await fetchContributors(org.login, repo.name, pat)
}))
Expand Down Expand Up @@ -131,7 +139,7 @@ export function AppProvider({ children }) {
return (
<Ctx.Provider value={{
pat, savePat, orgs, model, issuesData,
rateLimit, loading, loadMsg, govLoading, error,
rateLimit, loading, loadMsg, govLoading, error, totalRepo,
explore, runAudit, setError,
}}>
{children}
Expand Down
42 changes: 26 additions & 16 deletions src/pages/ContributorsPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useSortedData } from '../hooks/useSortedData'
import { computeBusFactor, exportContributorsCSV } from '../services/analytics'
import { useNavigate } from 'react-router-dom'
import EmptyStateCard from '../components/EmptyStateCard'
import { BsFillInfoSquareFill } from "react-icons/bs";
import { AiOutlineInfoCircle } from "react-icons/ai";

export default function ContributorsPage() {
const { model } = useApp()
Expand Down Expand Up @@ -90,12 +90,11 @@ export default function ContributorsPage() {
<p>Bus Factor Risk</p>

<button
onClick={() =>
setOpenInfo(openInfo === 'busfactor' ? null : 'busfactor')
}
className="p-2 rounded-full hover:bg-zinc-800 transition"
onMouseEnter={()=>setOpenInfo("busfactor")}
onMouseLeave={()=>setOpenInfo(null)}
className="p-2 rounded-full hover:bg-(--bg) transition"
>
<BsFillInfoSquareFill className="text-white cursor-pointer" />
<AiOutlineInfoCircle className="text-(--text) cursor-pointer" />
Comment thread
rahul-vyas-dev marked this conversation as resolved.
</button>

{openInfo === 'busfactor' && (
Expand Down Expand Up @@ -152,12 +151,11 @@ export default function ContributorsPage() {
<p>Freshness Index</p>

<button
onClick={() =>
setOpenInfo(openInfo === 'freshness' ? null : 'freshness')
}
className="p-2 rounded-full hover:bg-zinc-800 transition"
onMouseEnter={()=>setOpenInfo("freshness")}
onMouseLeave={()=>setOpenInfo(null)}
className="p-2 rounded-full hover:bg-(--bg) transition"
>
<BsFillInfoSquareFill className="text-white cursor-pointer" />
<AiOutlineInfoCircle className="text-(--text) cursor-pointer" />
</button>

{openInfo === 'freshness' && (
Expand Down Expand Up @@ -258,12 +256,11 @@ export default function ContributorsPage() {
<p>SIGNALS</p>

<button
onClick={() =>
setOpenInfo(openInfo === 'signals' ? null : 'signals')
}
className="p-2 rounded-full hover:bg-zinc-800 transition"
onMouseEnter={()=>setOpenInfo("signals")}
onMouseLeave={()=>setOpenInfo(null)}
className="p-2 rounded-full hover:bg-(--bg) transition"
>
<BsFillInfoSquareFill className="text-white cursor-pointer" />
<AiOutlineInfoCircle className="text-(--text) cursor-pointer" />
</button>

{openInfo === 'signals' && (
Expand Down Expand Up @@ -306,10 +303,23 @@ export default function ContributorsPage() {
{visible.map((c, i) => (
<tr key={c.login} style={{ borderBottom: '1px solid var(--border)', background: i % 2 ? 'var(--surface2)' : 'transparent' }}>
<td style={{ padding: '10px 14px' }}>
<a
href={`https://github.com/${c.login}`}
target="_blank"
rel="noopener noreferrer"
style={{
display: 'inline-flex',
alignItems: 'center',
gap: 8,
textDecoration: 'none',
color: 'inherit',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<img src={c.avatar_url} alt={c.login} style={{ width: 28, height: 28, borderRadius: '50%' }} />
<span style={{ fontSize: 13, fontWeight: 500 }}>{c.login}</span>
</div>
</a>
</td>
<td style={{ padding: '10px 14px' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/NetworkPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default function NetworkPage() {

// Top repos and contributors for performance
const topRepos = model.allRepos.slice(0, 30)
const topContribs = model.contributors.slice(0, 40)
const topContribs = model.contributors
Comment thread
rahul-vyas-dev marked this conversation as resolved.

const nodes = []
if (showRepos) topRepos.forEach(r => nodes.push({ id: `repo:${r.name}`, type: 'repo', data: r, ts: new Date(r.pushed_at).getTime() }))
Expand Down
22 changes: 12 additions & 10 deletions src/pages/OverviewPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import { useNavigate } from 'react-router-dom'
import { FiExternalLink, FiShare2, FiArrowRight } from 'react-icons/fi'
import { useApp } from '../context/AppContext'
import { C, StatCard, HealthBar } from '../components/UI'
import { BsFillInfoSquareFill } from "react-icons/bs";
import SocialShareButton from '../components/SocialShareButton';
import { AiOutlineInfoCircle } from "react-icons/ai";


const LANG_COLORS = ['#22c55e', '#f5c518', '#3b82f6', '#ef4444', '#a855f7', '#f97316', '#06b6d4']
const fmt = n => n > 999 ? (n / 1000).toFixed(1) + 'k' : String(n)

export default function OverviewPage() {
const { orgs, model } = useApp()
const { orgs, model, totalRepo } = useApp()
const navigate = useNavigate()
if (!model) return null

Expand Down Expand Up @@ -71,7 +72,7 @@ export default function OverviewPage() {
</div>
) : (
orgs[0]?.avatar_url && (
<img src={orgs[0].avatar_url} alt="" style={{ width: 56, height: 56, borderRadius: '50%', border: '2px solid var(--border)' }} />
<img src={orgs[0].avatar_url} alt="" style={{ width: 56, height: 56 }} />
Comment thread
rahul-vyas-dev marked this conversation as resolved.
)
)}
<div style={{ flex: 1 }}>
Expand Down Expand Up @@ -102,10 +103,10 @@ export default function OverviewPage() {

{/* Stats */}
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: 12, marginBottom: 24 }}>
<StatCard label="Total Repos" value={allRepos.length.toLocaleString()} />
<StatCard label="Total Repos" value={totalRepo.toLocaleString()} />
<StatCard label="Total Stars" value={fmt(totalStars)} />
<StatCard label="Total Forks" value={fmt(totalForks)} />
<StatCard label="Active Repos" value={activeRepos} sub={`${Math.round(activeRepos / allRepos.length * 100)}% of total`} />
<StatCard label="Active Repos" value={activeRepos} sub={`${Math.round(activeRepos / totalRepo * 100)}% of total`} />
Comment thread
rahul-vyas-dev marked this conversation as resolved.
</div>

{/* Language + top repos */}
Expand Down Expand Up @@ -138,16 +139,17 @@ export default function OverviewPage() {
<p>High Impact Repositories</p>

<button
onClick={() => setOpen(prev => !prev)}
className="p-3 rounded-full hover:bg-zinc-800 transition"
onMouseEnter={()=>setOpen(true)}
onMouseLeave={()=>setOpen(false)}
className="p-3 rounded-full hover:bg-(--bg) transition"
>
<BsFillInfoSquareFill />
<AiOutlineInfoCircle className="text-(--text) cursor-pointer" />
</button>
Comment thread
rahul-vyas-dev marked this conversation as resolved.
</div>

{open && (
<div
className="absolute top-16 right-2 w-80 z-50 rounded-lg border-2 border-(--border) bg-zinc-900 p-4 shadow-xl text-xs"
className="absolute top-16 right-2 w-80 z-50 rounded-lg border-2 border-(--border) bg-(--surface) p-4 shadow-xl text-xs"
>
<strong>Health Score</strong> estimates the overall health of a repository on a scale of 0 – 100.

Expand All @@ -163,7 +165,7 @@ export default function OverviewPage() {
</li>
</ul>

<p className="mt-2 text-zinc-400">
<p className="mt-2 text-xs text-(--text2)">
Higher scores indicate healthier and more actively maintained repositories.
</p>
</div>
Expand Down
Loading
Loading