From 4602d92986a7f80468718227775cfe67c757b327 Mon Sep 17 00:00:00 2001 From: Ritikydv1 Date: Sat, 13 Jun 2026 17:20:18 +0530 Subject: [PATCH 1/2] refactor: rename lifecycle classification to activity classification --- src/pages/OverviewPage.jsx | 4 ++-- src/services/analytics.js | 30 +++++++++++++++--------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/pages/OverviewPage.jsx b/src/pages/OverviewPage.jsx index c743007..5431db1 100644 --- a/src/pages/OverviewPage.jsx +++ b/src/pages/OverviewPage.jsx @@ -32,7 +32,7 @@ export default function OverviewPage() { const isMulti = orgs.length > 1 const totalStars = allRepos.reduce((s, r) => s + r.stargazers_count, 0) const totalForks = allRepos.reduce((s, r) => s + r.forks_count, 0) - const activeRepos = allRepos.filter(r => r.lifecycle === 'Thriving' || r.lifecycle === 'Stable').length + const activeRepos = allRepos.filter(r => r.activityClassification === 'Thriving' || r.activityClassification === 'Active').length const langMap = {} allRepos.forEach(r => { if (r.language) langMap[r.language] = (langMap[r.language] || 0) + 1 }) @@ -183,7 +183,7 @@ export default function OverviewPage() { {/* Nav cards */}
- + diff --git a/src/services/analytics.js b/src/services/analytics.js index acc40d7..09d1df6 100644 --- a/src/services/analytics.js +++ b/src/services/analytics.js @@ -1,4 +1,4 @@ -// Repo Health Indicator (Section 3.2.6) +// Repo Health Indicator // Activity (40%) + Issue Health (30%) + Diversity (30%) export function computeHealthScore(repo, contributorCount = 0) { const daysSince = (Date.now() - new Date(repo.pushed_at)) / 86_400_000 @@ -9,16 +9,16 @@ export function computeHealthScore(repo, contributorCount = 0) { return Math.round(activity * 0.4 + issueHealth * 0.3 + diversity * 0.3) } -// Repo Lifecycle (Section 3.2.6) — Thriving, Stable, Dormant, Abandoned based on recency of last push -export function computeLifecycle(repo) { +// Repo Lifecycle — Thriving, Active, Dormant, Hibernating based on recency of last push +export function computeActivityClassification(repo) { const days = (Date.now() - new Date(repo.pushed_at)) / 86_400_000 if (days <= 30) return 'Thriving' - if (days <= 90) return 'Stable' + if (days <= 90) return 'Active' if (days <= 180) return 'Dormant' - return 'Abandoned' + return 'Hibernating' } -// Bus Factor (Section 3.2.6) +// Bus Factor export function computeBusFactor(contributors = []) { if (!contributors.length) return { factor: 0, risk: 'unknown' } const total = contributors.reduce((s, c) => s + c.contributions, 0) @@ -34,9 +34,9 @@ export function computeBusFactor(contributors = []) { return { factor: contributors.length, risk: 'healthy' } } -// Unified Analytical Data Model (Section 3.2.0) +// Unified Analytical Data Model // Merges multiple orgs into one normalized graph: -// Organization → Repositories → Contributors → Issues/PRs +// Organization → Repositories → Contributors → Issues/PRs export function buildAnalyticalModel(orgs, reposPerOrg, contribsPerRepo) { const allRepos = [] const contributorMap = {} @@ -48,9 +48,9 @@ export function buildAnalyticalModel(orgs, reposPerOrg, contribsPerRepo) { const key = `${org.login}/${repo.name}` const contribs = contribsPerRepo[key] || [] const health = computeHealthScore(repo, contribs.length) - const lc = computeLifecycle(repo) + const activityClassification = computeActivityClassification(repo) const bf = computeBusFactor(contribs) - allRepos.push({ ...repo, orgLogin: org.login, contributors: contribs, healthScore: health, lifecycle: lc, busFactor: bf }) + allRepos.push({ ...repo, orgLogin: org.login, contributors: contribs, healthScore: health, activityClassification: activityClassification, busFactor: bf }) // Build contributor map — deduplicated by login across orgs contribs.forEach(c => { @@ -86,11 +86,11 @@ export function buildAnalyticalModel(orgs, reposPerOrg, contribsPerRepo) { : 0, })).sort((a, b) => b.totalContribs - a.totalContribs) - // Graph is constructed here and persisted through cache layers (Section 3.2.0) + // Graph is constructed here and persisted through cache layers return { allRepos, contributors } } -// Time-Series Bucketing (Section 3.2.9) +// Time-Series Bucketing // Parses created_at, closed_at, merged_at into weekly/monthly bins export function buildTimeSeries(issues = [], granularity = 'monthly') { const buckets = {} @@ -142,7 +142,7 @@ export function buildTimeSeries(issues = [], granularity = 'monthly') { .slice(-12) } -// CSV Export (Section 3.2.9) +// CSV Export function download(content, filename, type = 'text/csv') { const blob = new Blob([content], { type }) const url = URL.createObjectURL(blob) @@ -152,8 +152,8 @@ function download(content, filename, type = 'text/csv') { } export function exportReposCSV(repos) { - const header = ['Repository','Org','Stars','Forks','Open Issues','Health Score','Lifecycle','Language','Last Active'] - const rows = repos.map(r => [r.name, r.orgLogin, r.stargazers_count, r.forks_count, r.open_issues_count, r.healthScore, r.lifecycle, r.language || 'N/A', r.pushed_at?.slice(0, 10)]) + const header = ['Repository','Org','Stars','Forks','Open Issues','Health Score','Activity Classification','Language','Last Active'] + const rows = repos.map(r => [r.name, r.orgLogin, r.stargazers_count, r.forks_count, r.open_issues_count, r.healthScore, r.activityClassification, r.language || 'N/A', r.pushed_at?.slice(0, 10)]) download([header, ...rows].map(r => r.join(',')).join('\n'), 'orgexplorer-repos.csv') } From 7aef67f3d2ebed2e128c96dea88695432affdb21 Mon Sep 17 00:00:00 2001 From: Ritikydv1 Date: Sat, 13 Jun 2026 17:41:56 +0530 Subject: [PATCH 2/2] refactor: replace lifecycle classification with activity classification across UI --- src/components/UI.jsx | 6 ++--- src/pages/RepositoriesPage.jsx | 44 ++++++++++++++++------------------ 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/components/UI.jsx b/src/components/UI.jsx index f53b292..0dae324 100644 --- a/src/components/UI.jsx +++ b/src/components/UI.jsx @@ -63,12 +63,12 @@ export const C = { }, } -// Lifecycle badge color map +// Activity badge color map const LC = { Thriving: ['#22c55e', 'rgba(34,197,94,.15)'], - Stable: ['#3b82f6', 'rgba(59,130,246,.15)'], + Active: ['#3b82f6', 'rgba(59,130,246,.15)'], Dormant: ['#f59e0b', 'rgba(245,158,11,.15)'], - Abandoned: ['#ef4444', 'rgba(239,68,68,.15)'], + Hibernating: ['#ef4444', 'rgba(239,68,68,.15)'], critical: ['#ef4444', 'rgba(239,68,68,.15)'], high: ['#f59e0b', 'rgba(245,158,11,.15)'], healthy: ['#22c55e', 'rgba(34,197,94,.15)'], diff --git a/src/pages/RepositoriesPage.jsx b/src/pages/RepositoriesPage.jsx index a77eb4f..d551094 100644 --- a/src/pages/RepositoriesPage.jsx +++ b/src/pages/RepositoriesPage.jsx @@ -8,13 +8,13 @@ import { exportReposCSV } from '../services/analytics' import EmptyStateCard from '../components/EmptyStateCard' import { useNavigate } from 'react-router-dom' -const LIFECYCLES = ['All', 'Thriving', 'Stable', 'Dormant', 'Abandoned'] -const LC_ACTIVE = { Thriving: 'var(--green)', Stable: 'var(--blue)', Dormant: 'var(--amber)', Abandoned: 'var(--red)' } +const ACTIVITY_CLASSIFICATIONS = ['All', 'Thriving', 'Active', 'Dormant', 'Hibernating'] +const ACTIVITY_COLORS = { Thriving: 'var(--green)', Active: 'var(--blue)', Dormant: 'var(--amber)', Hibernating: 'var(--red)' } export default function RepositoriesPage() { const { model } = useApp() const [search, setSearch] = useState('') - const [lifecycle, setLifecycle] = useState('All') + const [activityClassification, setActivityClassification] = useState('All') const [lang, setLang] = useState('All') const [view, setView] = useState('grid') const [shown, setShown] = useState(20) @@ -45,11 +45,11 @@ export default function RepositoriesPage() { [allRepos]) const filtered = useMemo(() => allRepos.filter(r => - (lifecycle === 'All' || r.lifecycle === lifecycle) && + (activityClassification === 'All' || r.activityClassification === activityClassification) && (lang === 'All' || r.language === lang) && (!search || r.name.toLowerCase().includes(search.toLowerCase()) || (r.description || '').toLowerCase().includes(search.toLowerCase())) - ), [allRepos, lifecycle, lang, search]) + ), [allRepos, activityClassification, lang, search]) const { sorted, sortConfig, onSort } = useSortedData(filtered, 'healthScore', 'desc') const visible = sorted.slice(0, shown) @@ -60,7 +60,7 @@ export default function RepositoriesPage() { ['forks_count', 'Forks'], ['open_issues_count', 'Open Issues'], ['healthScore', 'Health'], - ['lifecycle', 'Lifecycle'], + ['activityClassification', 'Activity Classification'], ['pushed_at', 'Last Push'], ] @@ -80,7 +80,7 @@ export default function RepositoriesPage() {
} - subtitle="Technical health and lifecycle across all repositories in the portfolio" + subtitle="Repository insights and activity classification across all repositories." right={ {filtered.length} @@ -117,8 +117,8 @@ export default function RepositoriesPage() {

- OrgExplorer evaluates repositories using activity, issue health, - contributor diversity, and lifecycle status. + OrgExplorer evaluates repositories using issue health, + contributor diversity, and activity classification status.

@@ -129,12 +129,12 @@ export default function RepositoriesPage() {
  • Contributor Diversity → 30%
  • - Lifecycle Classification + Activity Classification
    • 🟢 Thriving → Updated within 30 days
    • -
    • 🔵 Stable → Updated within 90 days
    • +
    • 🔵 Active → Updated within 90 days
    • 🟡 Dormant → Updated within 180 days
    • -
    • 🔴 Abandoned → No updates for 180+ days
    • +
    • 🔴 Hibernating → No updates for 180+ days
    Repository Signals @@ -172,14 +172,14 @@ export default function RepositoriesPage() {
    - {LIFECYCLES.map(l => ( + {ACTIVITY_CLASSIFICATIONS.map(l => (

    {r.description || 'No description provided'}