Refactor/lifecycle to activity classification#74
Conversation
WalkthroughThis 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. ChangesActivity Classification System
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (4)
src/components/UI.jsxsrc/pages/OverviewPage.jsxsrc/pages/RepositoriesPage.jsxsrc/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" /> |
There was a problem hiding this comment.
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." |
There was a problem hiding this comment.
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-L122src/pages/RepositoriesPage.jsx#L132-L137src/pages/RepositoriesPage.jsx#L156-L156src/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> |
There was a problem hiding this comment.
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.
| 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') |
There was a problem hiding this comment.
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.
Addressed Issues:
Fixes #
Screenshots/Recordings:
Before

After

Additional Notes:
Checklist
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