mirror of
https://github.com/HeyPuter/puter.git
synced 2026-11-05 13:35:20 +00:00
fix(dashboard): don't show a failed usage request as zero usage
getMonthlyUsage() resolves with the error body when the request fails, and Usage and Home read it as a month with nothing used: $0.00 of $0.00, 0%, and on Usage an empty table replacing the one already on screen. A body without a usage report is treated as the failure it is, so the last good numbers stay and a first load shows the "unavailable" note.
This commit is contained in:
1 parent
d364509b91
commit
4c29f7268f
4 files changed
+34
-5
No files matched your search
@@ -19,7 +19,7 @@
|
||||
|
||||
import UIWindowSaveAccount from '../UIWindowSaveAccount.js';
|
||||
import { formatCredits, formatDollarsFromMicrocents, usageIsCredits } from './credits.js';
|
||||
import { addonCreditsRemaining, usageBudget } from './usageBudget.js';
|
||||
import { addonCreditsRemaining, monthlyUsageOrThrow, usageBudget } from './usageBudget.js';
|
||||
import { appIconAttrs } from '../../helpers/appIcon.js';
|
||||
import { filterLaunchableApps } from '../../helpers/launchAppsList.js';
|
||||
import { isOrgSeat, orgSeatTeamName } from './orgSeat.js';
|
||||
@@ -644,7 +644,7 @@ const TabHome = {
|
||||
|
||||
// Load monthly usage data
|
||||
try {
|
||||
const res = await puter.auth.getMonthlyUsage();
|
||||
const res = monthlyUsageOrThrow(await puter.auth.getMonthlyUsage());
|
||||
const budget = usageBudget(res.usage, res.allowanceInfo);
|
||||
const addonLeft = addonCreditsRemaining(res.allowanceInfo);
|
||||
// The server reports credits (already scaled) or raw amounts
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
import { formatCredits, formatDollarsFromMicrocents, usageIsCredits } from './credits.js';
|
||||
import { isOrgSeat } from './orgSeat.js';
|
||||
import { addonCreditsRemaining, usageBudget } from './usageBudget.js';
|
||||
import { addonCreditsRemaining, monthlyUsageOrThrow, usageBudget } from './usageBudget.js';
|
||||
|
||||
// Whether the server reported credits — decides whether the usage surfaces
|
||||
// render credits or fall back to dollars.
|
||||
@@ -293,7 +293,7 @@ function renderUsageTable () {
|
||||
}
|
||||
|
||||
async function update_usage_details ($el_window) {
|
||||
const monthlyUsagePromise = puter.auth.getMonthlyUsage().then(res => {
|
||||
const monthlyUsagePromise = puter.auth.getMonthlyUsage().then(monthlyUsageOrThrow).then(res => {
|
||||
const budget = usageBudget(res.usage, res.allowanceInfo);
|
||||
const addonLeft = addonCreditsRemaining(res.allowanceInfo);
|
||||
// The server reports credits (already scaled) or raw amounts (no
|
||||
|
||||
@@ -74,3 +74,18 @@ export const addonCreditsRemaining = (allowanceInfo) => {
|
||||
if ( ! (purchased > 0) ) return null;
|
||||
return Math.max(0, purchased - nonNegative(addons.consumedPurchaseCredits));
|
||||
};
|
||||
|
||||
/**
|
||||
* `getMonthlyUsage()` resolves with whatever body the server answered, an
|
||||
* error included. Throws unless that body is a usage report, so a failed
|
||||
* request isn't shown as a month with nothing used.
|
||||
*
|
||||
* @param {Object | null | undefined} res - What `getMonthlyUsage()` resolved with.
|
||||
* @returns {Object} `res`, once checked.
|
||||
*/
|
||||
export const monthlyUsageOrThrow = (res) => {
|
||||
if ( res === null || typeof res?.usage !== 'object' || res.usage === null ) {
|
||||
throw new Error(res?.message || 'Usage report unavailable');
|
||||
}
|
||||
return res;
|
||||
};
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { addonCreditsRemaining, usageBudget } from './usageBudget.js';
|
||||
import { addonCreditsRemaining, monthlyUsageOrThrow, usageBudget } from './usageBudget.js';
|
||||
|
||||
const info = (allowance, addons = {}) => ({
|
||||
monthUsageAllowance: allowance,
|
||||
@@ -147,3 +147,17 @@ describe('addonCreditsRemaining', () => {
|
||||
}))).toBe(1_000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('monthlyUsageOrThrow', () => {
|
||||
it('passes a usage report through', () => {
|
||||
const res = { usage: { total: 0 }, allowanceInfo: null };
|
||||
expect(monthlyUsageOrThrow(res)).toBe(res);
|
||||
});
|
||||
|
||||
it('throws on an error body instead of reading it as zero usage', () => {
|
||||
expect(() => monthlyUsageOrThrow({ message: 'boom' })).toThrow('boom');
|
||||
expect(() => monthlyUsageOrThrow({ usage: null })).toThrow();
|
||||
expect(() => monthlyUsageOrThrow(null)).toThrow();
|
||||
expect(() => monthlyUsageOrThrow(undefined)).toThrow();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user