Skip to content

Refactor/lifecycle to activity classification#74

Open
Ri1tik wants to merge 2 commits into
AOSSIE-Org:mainfrom
Ri1tik:refactor/lifecycle-to-activity-classification
Open

Refactor/lifecycle to activity classification#74
Ri1tik wants to merge 2 commits into
AOSSIE-Org:mainfrom
Ri1tik:refactor/lifecycle-to-activity-classification

Conversation

@Ri1tik

@Ri1tik Ri1tik commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Addressed Issues:

Fixes #

Screenshots/Recordings:

Before
image

After
image

Additional Notes:

Checklist

  • My code follows the project's code style and conventions
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings or errors
  • I have joined the Discord server and I will share a link to this PR with the project maintainers there
  • I have read the Contributing Guidelines

⚠️ AI Notice - Important!

We encourage contributors to use AI tools responsibly when creating Pull Requests. While AI can be a valuable aid, it is essential to ensure that your contributions meet the task requirements, build successfully, include relevant tests, and pass all linters. Submissions that do not meet these standards may be closed without warning to maintain the quality and integrity of the project. Please take the time to understand the changes you are proposing and their impact.

Summary by CodeRabbit

  • New Features
    • Updated repository classification system from "Lifecycle" to "Activity Classification" terminology throughout the application.
    • Introduced new classification states: "Active" (replacing "Stable"), "Hibernating" (new), "Thriving," and "Dormant."
    • Updated repository filtering, badges, and status displays to reflect the new activity classification system.

@github-actions github-actions Bot added the no-issue-linked PR has no linked issue label Jun 13, 2026
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR systematically replaces the lifecycle classification system with activity classification throughout OrgExplorer. The core analytics logic computes activity status based on repository push recency, updating data models and CSV exports. UI components and pages adopt the new classification for filtering, display, and metrics.

Changes

Activity Classification System

Layer / File(s) Summary
Analytics Foundation - Activity Classification Logic
src/services/analytics.js
Replace computeLifecycle() with computeActivityClassification() that classifies repos as Thriving/Active/Dormant/Hibernating based on days since last push. Update buildAnalyticalModel() to compute and store activityClassification instead of lifecycle, and sync CSV export to use the new field.
Badge Styling for Activity Classifications
src/components/UI.jsx
Update Badge color mappings: rename Stable to Active, remove Abandoned, add Hibernating; retain Thriving, Dormant, and severity-like keys.
Repositories Page - Classification Filtering and Display
src/pages/RepositoriesPage.jsx
Introduce ACTIVITY_CLASSIFICATIONS and ACTIVITY_COLORS constants with activityClassification state. Replace lifecycle filtering logic with activity classification predicate. Update table column, popover legend (Thriving/Active/Dormant/Hibernating), filter buttons, and grid/list view rendering to use r.activityClassification and apply ACTIVITY_COLORS to borders.
Overview Page - Active Repos Count and Navigation
src/pages/OverviewPage.jsx
Update activeRepos filter to count repositories with activityClassification Thriving or Active. Update Repositories navigation card subtitle to reference activity classification state.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested labels

Typescript Lang

Suggested reviewers

  • bhavik-mangla
  • Zahnentferner

Poem

🐰 From lifecycle to activity bright,
Repos classified by push recency's light—
Thriving and Active lead the way,
While Dormant and Hibernating gently sway.
A cleaner classification, hooray!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly describes the main change: refactoring terminology from lifecycle classification to activity classification, which aligns with all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added frontend Frontend changes javascript JavaScript/TypeScript changes size/M 51-200 lines changed external-contributor External contributor and removed size/M 51-200 lines changed labels Jun 13, 2026
@Ri1tik Ri1tik added the gsoc GSoC students label Jun 13, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pages/OverviewPage.jsx`:
- Line 186: The hardcoded user-visible subtitle passed as the sub prop on the
NavCard in the OverviewPage component should be moved into the i18n resource
bundle: add a new translation key (e.g. "overview.nav.repositories.sub") to your
locale files with value "Explore and sort repos by health, and activity
classification state", then replace the inline literal in the OverviewPage JSX
(the NavCard usage that sets sub="...") with an i18n lookup (e.g.
t('overview.nav.repositories.sub') or the project's translate helper). Ensure
the locale file is saved/committed and that any required import
(useTranslation/t) is added to OverviewPage.jsx so the key resolves at runtime;
adjust snapshot/tests if they assert the literal.

In `@src/pages/RepositoriesPage.jsx`:
- Line 137: The copy in RepositoriesPage.jsx incorrectly says "180+ days" while
the classifier uses "> 180" for Hibernating; update the list item text in the
RepositoriesPage component (the string "🔴 Hibernating → No updates for 180+
days") to "🔴 Hibernating → No updates for more than 180 days" (or "181+ days")
so the wording matches the classifier logic.
- Line 83: Move all hardcoded user-visible strings into i18n resource keys and
use the localization helper in the RepositoriesPage component and analytics
export: in src/pages/RepositoriesPage.jsx (lines 83-83) replace the subtitle
literal with a call to the i18n lookup (e.g., t('repositories.subtitle')); in
the same file (lines 120-122) replace the popover explanatory text with
t('repositories.classificationPopover'); in that file (lines 132-137) replace
the classification legend labels and descriptions with
t('repositories.classification.legend.*') keys; in that file (line 156) move the
input placeholder to t('repositories.filter.placeholder'); and in
src/services/analytics.js (lines 155-156) replace hardcoded CSV header labels
with i18n-based labels (e.g., import the same i18n helper or a shared
localization-aware CSV_LABELS map) so exports use localized strings; add
corresponding keys to the i18n resource file.

In `@src/services/analytics.js`:
- Around line 156-157: Sanitize CSV cells before joining: update the rows
creation (the const rows = repos.map(...) line) so each field extracted from r
(e.g., r.name, r.orgLogin, r.stargazers_count, r.forks_count,
r.open_issues_count, r.healthScore, r.activityClassification, r.language,
r.pushed_at) is converted to a string, null/undefined normalized (e.g., to empty
string or 'N/A'), and any value that begins with =, +, -, or @ is escaped by
prefixing a single quote (') or otherwise neutralizing the leading character,
then pass those sanitized strings into the existing
download([...].map(...).join('\n')) call; ensure you apply this sanitization
consistently for every cell in the rows array so no untrusted field is exported
raw.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0b634ab8-1931-4c43-ac03-eae69bc703aa

📥 Commits

Reviewing files that changed from the base of the PR and between 33fd4fc and 7aef67f.

📒 Files selected for processing (4)
  • src/components/UI.jsx
  • src/pages/OverviewPage.jsx
  • src/pages/RepositoriesPage.jsx
  • src/services/analytics.js

{/* Nav cards */}
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 14 }}>
<NavCard to="/repositories" label="Repositories" sub="Explore and sort repos by health, activity, and lifecycle state" />
<NavCard to="/repositories" label="Repositories" sub="Explore and sort repos by health, and activity classification state" />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Externalize this new nav subtitle string to i18n resources.

Line 186 introduces a new user-facing literal directly in JSX. Please move it to your translation/resource layer instead of hardcoding it inline.

Suggested change
-        <NavCard to="/repositories" label="Repositories" sub="Explore and sort repos by health, and activity classification state" />
+        <NavCard
+          to="/repositories"
+          label={t('overview.nav.repositories.label')}
+          sub={t('overview.nav.repositories.sub')}
+        />

As per coding guidelines, "User-visible strings should be externalized to resource files (i18n)".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/OverviewPage.jsx` at line 186, The hardcoded user-visible subtitle
passed as the sub prop on the NavCard in the OverviewPage component should be
moved into the i18n resource bundle: add a new translation key (e.g.
"overview.nav.repositories.sub") to your locale files with value "Explore and
sort repos by health, and activity classification state", then replace the
inline literal in the OverviewPage JSX (the NavCard usage that sets sub="...")
with an i18n lookup (e.g. t('overview.nav.repositories.sub') or the project's
translate helper). Ensure the locale file is saved/committed and that any
required import (useTranslation/t) is added to OverviewPage.jsx so the key
resolves at runtime; adjust snapshot/tests if they assert the literal.

Source: Coding guidelines

</div>
}
subtitle="Technical health and lifecycle across all repositories in the portfolio"
subtitle="Repository insights and activity classification across all repositories."

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Centralized i18n gap: newly introduced classification copy is hardcoded across UI and exports.

The same root cause appears in multiple changed sites: new user-facing strings are embedded directly instead of being sourced from localization resources.

  • src/pages/RepositoriesPage.jsx#L83-L83: move subtitle text to i18n keys.
  • src/pages/RepositoriesPage.jsx#L120-L122: move explanatory popover text to i18n keys.
  • src/pages/RepositoriesPage.jsx#L132-L137: move classification legend labels/descriptions to i18n keys.
  • src/pages/RepositoriesPage.jsx#L156-L156: move input placeholder text to i18n keys.
  • src/services/analytics.js#L155-L156: move CSV header labels to i18n resources (or a shared localization-aware export label map).

As per coding guidelines, "User-visible strings should be externalized to resource files (i18n)."

📍 Affects 2 files
  • src/pages/RepositoriesPage.jsx#L83-L83 (this comment)
  • src/pages/RepositoriesPage.jsx#L120-L122
  • src/pages/RepositoriesPage.jsx#L132-L137
  • src/pages/RepositoriesPage.jsx#L156-L156
  • src/services/analytics.js#L155-L156
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/RepositoriesPage.jsx` at line 83, Move all hardcoded user-visible
strings into i18n resource keys and use the localization helper in the
RepositoriesPage component and analytics export: in
src/pages/RepositoriesPage.jsx (lines 83-83) replace the subtitle literal with a
call to the i18n lookup (e.g., t('repositories.subtitle')); in the same file
(lines 120-122) replace the popover explanatory text with
t('repositories.classificationPopover'); in that file (lines 132-137) replace
the classification legend labels and descriptions with
t('repositories.classification.legend.*') keys; in that file (line 156) move the
input placeholder to t('repositories.filter.placeholder'); and in
src/services/analytics.js (lines 155-156) replace hardcoded CSV header labels
with i18n-based labels (e.g., import the same i18n helper or a shared
localization-aware CSV_LABELS map) so exports use localized strings; add
corresponding keys to the i18n resource file.

Source: Coding guidelines

<li>🔵 Active → Updated within 90 days</li>
<li>🟡 Dormant → Updated within 180 days</li>
<li>🔴 Abandoned → No updates for 180+ days</li>
<li>🔴 Hibernating → No updates for 180+ days</li>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix the Hibernating threshold wording to match the actual classifier logic.

Line 137 says “180+ days”, but the classifier marks <= 180 as Dormant and only > 180 as Hibernating. Update copy to “more than 180 days” (or “181+ days”).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/RepositoriesPage.jsx` at line 137, The copy in RepositoriesPage.jsx
incorrectly says "180+ days" while the classifier uses "> 180" for Hibernating;
update the list item text in the RepositoriesPage component (the string "🔴
Hibernating → No updates for 180+ days") to "🔴 Hibernating → No updates for
more than 180 days" (or "181+ days") so the wording matches the classifier
logic.

Comment thread src/services/analytics.js
Comment on lines +156 to 157
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')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Sanitize CSV cells to prevent spreadsheet formula injection.

Line 156 exports untrusted repository fields directly into CSV. Values beginning with =, +, -, or @ can execute formulas when opened in spreadsheet tools.

Suggested fix
+function escapeCsvCell(value) {
+  const s = String(value ?? '')
+  const prefixed = /^[=+\-@]/.test(s) ? `'${s}` : s
+  return `"${prefixed.replace(/"/g, '""')}"`
+}
+
 export function exportReposCSV(repos) {
   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')
+  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(row => row.map(escapeCsvCell).join(',')).join('\n'),
+    'orgexplorer-repos.csv'
+  )
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/analytics.js` around lines 156 - 157, Sanitize CSV cells before
joining: update the rows creation (the const rows = repos.map(...) line) so each
field extracted from r (e.g., r.name, r.orgLogin, r.stargazers_count,
r.forks_count, r.open_issues_count, r.healthScore, r.activityClassification,
r.language, r.pushed_at) is converted to a string, null/undefined normalized
(e.g., to empty string or 'N/A'), and any value that begins with =, +, -, or @
is escaped by prefixing a single quote (') or otherwise neutralizing the leading
character, then pass those sanitized strings into the existing
download([...].map(...).join('\n')) call; ensure you apply this sanitization
consistently for every cell in the rows array so no untrusted field is exported
raw.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external-contributor External contributor frontend Frontend changes gsoc GSoC students javascript JavaScript/TypeScript changes no-issue-linked PR has no linked issue size/M 51-200 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant