Skip to content

Commit 0c97c14

Browse files
authored
fix: return null deltaPercentage when previous month has zero contributions (JhaSourav07#913)
Fixes JhaSourav07#908 ## Description `calculateMonthlyStats` in `lib/calculate.ts` hardcoded `deltaPercentage = 100` whenever `previousMonthTotal === 0`, regardless of how many contributions were made this month. This is mathematically undefined — dividing by zero has no meaningful percentage result. A user with 1 commit vs 0 last month and a user with 500 commits vs 0 last month both saw `+100%`, which is misleading and factually incorrect. **Changes:** - `deltaPercentage` is now typed as `number | null` in `MonthlyStats` (`types/index.ts`) - `calculateMonthlyStats` returns `null` when `previousMonthTotal === 0` instead of hardcoded `100` - `generateMonthlySVG` and `generateAutoThemeMonthlySVG` now render `N/A` when `deltaPercentage` is `null` - Updated existing test in `lib/calculate.test.ts` to expect `null` instead of `100` **Files changed:** `lib/calculate.ts`, `lib/svg/generator.ts`, `types/index.ts`, `lib/calculate.test.ts` ## Pillar - [x] 🛠️ Other (Bug fix, refactoring, docs) ## Visual Preview N/A — delta indicator will now show `N/A` instead of `+100%` for users with zero contributions last month. ## Checklist before requesting a review: - [x] I have read the `CONTRIBUTING.md` file. - [x] I have tested these changes locally. - [x] I have run `npm run format` and `npm run lint` locally and resolved all errors. - [x] My commits follow the Conventional Commits format. - [ ] I have updated `README.md` if I added a new theme or URL parameter. - [x] I have starred the repo. - [x] I have made sure that I have only one commit to merge in this PR. - [x] The SVG output matches the CommitPulse "premium quality" aesthetic standard. - [ ] (Recommended) I joined the CommitPulse Discord community.
2 parents 4e45398 + 9096643 commit 0c97c14

4 files changed

Lines changed: 41 additions & 33 deletions

File tree

lib/calculate.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ describe('calculateMonthlyStats', () => {
376376

377377
expect(result.previousMonthTotal).toBe(0);
378378
expect(result.currentMonthTotal).toBe(10);
379-
expect(result.deltaPercentage).toBe(100);
379+
expect(result.deltaPercentage).toBeNull();
380380
});
381381

382382
it('handles zero current month contributions', () => {

lib/calculate.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,16 +121,16 @@ export function calculateMonthlyStats(
121121
}).format(now);
122122

123123
const deltaAbsolute = currentMonthTotal - previousMonthTotal;
124-
let deltaPercentage = 0;
125-
126-
if (previousMonthTotal === 0) {
127-
if (currentMonthTotal > 0) {
128-
deltaPercentage = 100;
129-
}
130-
} else {
131-
deltaPercentage = Math.round((deltaAbsolute / previousMonthTotal) * 100);
132-
if (deltaPercentage === -0) deltaPercentage = 0;
133-
}
124+
// When there is no baseline (previous month = 0), the percentage change is
125+
// mathematically undefined. Return null so the renderer can display 'N/A'
126+
// instead of the misleading hardcoded +100%.
127+
const deltaPercentage: number | null =
128+
previousMonthTotal === 0
129+
? null
130+
: (() => {
131+
const pct = Math.round((deltaAbsolute / previousMonthTotal) * 100);
132+
return pct === -0 ? 0 : pct;
133+
})();
134134

135135
return {
136136
currentMonthTotal,

lib/svg/generator.ts

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -392,18 +392,22 @@ export function generateMonthlySVG(stats: MonthlyStats, params: BadgeParams): st
392392
: `${stats.deltaAbsolute} ${deltaUnit}`;
393393
} else if (params.delta_format === 'both') {
394394
deltaText =
395-
stats.deltaPercentage > 0
396-
? `+${stats.deltaPercentage}% (+${stats.deltaAbsolute})`
397-
: stats.deltaPercentage < 0
398-
? `${stats.deltaPercentage}% (${stats.deltaAbsolute})`
399-
: `0% (${stats.deltaAbsolute > 0 ? '+' : ''}${stats.deltaAbsolute})`;
395+
stats.deltaPercentage === null
396+
? `N/A (${stats.deltaAbsolute > 0 ? '+' : ''}${stats.deltaAbsolute})`
397+
: stats.deltaPercentage > 0
398+
? `+${stats.deltaPercentage}% (+${stats.deltaAbsolute})`
399+
: stats.deltaPercentage < 0
400+
? `${stats.deltaPercentage}% (${stats.deltaAbsolute})`
401+
: `0% (${stats.deltaAbsolute > 0 ? '+' : ''}${stats.deltaAbsolute})`;
400402
} else {
401403
deltaText =
402-
stats.deltaPercentage > 0
403-
? `+${stats.deltaPercentage}%`
404-
: stats.deltaPercentage < 0
405-
? `${stats.deltaPercentage}%`
406-
: `0%`;
404+
stats.deltaPercentage === null
405+
? 'N/A'
406+
: stats.deltaPercentage > 0
407+
? `+${stats.deltaPercentage}%`
408+
: stats.deltaPercentage < 0
409+
? `${stats.deltaPercentage}%`
410+
: `0%`;
407411
}
408412
const deltaColor = stats.deltaAbsolute >= 0 ? accent : '#ff4444';
409413

@@ -475,18 +479,22 @@ function generateAutoThemeMonthlySVG(stats: MonthlyStats, params: BadgeParams):
475479
: `${stats.deltaAbsolute} ${deltaUnit}`;
476480
} else if (params.delta_format === 'both') {
477481
deltaText =
478-
stats.deltaPercentage > 0
479-
? `+${stats.deltaPercentage}% (+${stats.deltaAbsolute})`
480-
: stats.deltaPercentage < 0
481-
? `${stats.deltaPercentage}% (${stats.deltaAbsolute})`
482-
: `0% (${stats.deltaAbsolute > 0 ? '+' : ''}${stats.deltaAbsolute})`;
482+
stats.deltaPercentage === null
483+
? `N/A (${stats.deltaAbsolute > 0 ? '+' : ''}${stats.deltaAbsolute})`
484+
: stats.deltaPercentage > 0
485+
? `+${stats.deltaPercentage}% (+${stats.deltaAbsolute})`
486+
: stats.deltaPercentage < 0
487+
? `${stats.deltaPercentage}% (${stats.deltaAbsolute})`
488+
: `0% (${stats.deltaAbsolute > 0 ? '+' : ''}${stats.deltaAbsolute})`;
483489
} else {
484490
deltaText =
485-
stats.deltaPercentage > 0
486-
? `+${stats.deltaPercentage}%`
487-
: stats.deltaPercentage < 0
488-
? `${stats.deltaPercentage}%`
489-
: `0%`;
491+
stats.deltaPercentage === null
492+
? 'N/A'
493+
: stats.deltaPercentage > 0
494+
? `+${stats.deltaPercentage}%`
495+
: stats.deltaPercentage < 0
496+
? `${stats.deltaPercentage}%`
497+
: `0%`;
490498
}
491499

492500
return `

types/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ export interface MonthlyStats {
8383
/** Total number of contributions in the previous calendar month. */
8484
previousMonthTotal: number;
8585

86-
/** Percentage change in contributions compared to the previous month (can be negative). */
87-
deltaPercentage: number;
86+
/** Percentage change in contributions compared to the previous month (can be negative). Null when previous month has zero contributions (undefined baseline). */
87+
deltaPercentage: number | null;
8888

8989
/** Absolute change in contribution count compared to the previous month (can be negative). */
9090
deltaAbsolute: number;

0 commit comments

Comments
 (0)