Improve byte formatting across the UI
This commit is contained in:
@@ -49,3 +49,4 @@ Updated: 2026-04-02
|
|||||||
32. Restored proxy-link copying for plain-`http` deployments by falling back from the Clipboard API to `execCommand('copy')`, and added regression coverage for both clipboard paths.
|
32. Restored proxy-link copying for plain-`http` deployments by falling back from the Clipboard API to `execCommand('copy')`, and added regression coverage for both clipboard paths.
|
||||||
33. Replaced stale startup mock values with explicit skeleton loading states so the shell no longer flashes fallback dashboard/users/settings data before the first live snapshot arrives.
|
33. Replaced stale startup mock values with explicit skeleton loading states so the shell no longer flashes fallback dashboard/users/settings data before the first live snapshot arrives.
|
||||||
34. Renamed the tracked Docker Compose file to `compose.example.yml` and ignored local `compose.yml` so production-specific compose settings no longer get overwritten by pulls.
|
34. Renamed the tracked Docker Compose file to `compose.example.yml` and ignored local `compose.yml` so production-specific compose settings no longer get overwritten by pulls.
|
||||||
|
35. Expanded byte formatting to cover `KB` through `PB`, improving dashboard totals, user usage, and quota remainder readability for both small and very large traffic values.
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ Updated: 2026-04-02
|
|||||||
- `src/App.test.tsx`: login-gate, startup skeleton, preferences persistence, hash-tab restoration, websocket-sync safety, reconnect/logout fallback handling, clipboard fallback coverage, generated-credential/create-edit modal flows, runtime stop, pause/resume, delete-confirm, and settings-save UI tests
|
- `src/App.test.tsx`: login-gate, startup skeleton, preferences persistence, hash-tab restoration, websocket-sync safety, reconnect/logout fallback handling, clipboard fallback coverage, generated-credential/create-edit modal flows, runtime stop, pause/resume, delete-confirm, and settings-save UI tests
|
||||||
- `src/app.css`: full panel styling including fixed-width icon action buttons, busy-state treatment, startup skeleton shimmer states, and connection banner styling
|
- `src/app.css`: full panel styling including fixed-width icon action buttons, busy-state treatment, startup skeleton shimmer states, and connection banner styling
|
||||||
- `src/data/mockDashboard.ts`: default panel state and frontend fallback snapshot
|
- `src/data/mockDashboard.ts`: default panel state and frontend fallback snapshot
|
||||||
- `src/lib/3proxy.ts`: formatting and status helpers
|
- `src/lib/3proxy.ts`: formatting and status helpers, including byte scaling from `B` through `PB`
|
||||||
- `src/lib/3proxy.test.ts`: paranoia-oriented tests for core domain rules
|
- `src/lib/3proxy.test.ts`: paranoia-oriented tests for core domain rules and byte-format scaling boundaries
|
||||||
- `src/lib/panelPreferences.ts`: `localStorage`-backed panel language/theme preferences plus theme application helpers with `system` as the default theme
|
- `src/lib/panelPreferences.ts`: `localStorage`-backed panel language/theme preferences plus theme application helpers with `system` as the default theme
|
||||||
- `src/lib/panelText.ts`: English/Russian UI text catalog for the panel shell, user-edit actions, runtime controls, last-seen labels, and connection recovery notices
|
- `src/lib/panelText.ts`: English/Russian UI text catalog for the panel shell, user-edit actions, runtime controls, last-seen labels, and connection recovery notices
|
||||||
- `src/shared/contracts.ts`: shared panel, service, user, and API data contracts including per-user last-seen metadata
|
- `src/shared/contracts.ts`: shared panel, service, user, and API data contracts including per-user last-seen metadata
|
||||||
|
|||||||
@@ -13,10 +13,16 @@ describe('formatBytes', () => {
|
|||||||
expect(formatBytes(-50)).toBe('0 B');
|
expect(formatBytes(-50)).toBe('0 B');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('formats megabytes and gigabytes with stable precision', () => {
|
it('formats kilobytes, megabytes, and gigabytes with stable precision', () => {
|
||||||
|
expect(formatBytes(122589)).toBe('119.7 KB');
|
||||||
expect(formatBytes(10 * 1024 * 1024)).toBe('10.0 MB');
|
expect(formatBytes(10 * 1024 * 1024)).toBe('10.0 MB');
|
||||||
expect(formatBytes(2.5 * 1024 * 1024 * 1024)).toBe('2.50 GB');
|
expect(formatBytes(2.5 * 1024 * 1024 * 1024)).toBe('2.50 GB');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('scales cleanly through terabytes and petabytes', () => {
|
||||||
|
expect(formatBytes(3.2 * 1024 ** 4)).toBe('3.20 TB');
|
||||||
|
expect(formatBytes(1.25 * 1024 ** 5)).toBe('1.25 PB');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('buildProxyLink', () => {
|
describe('buildProxyLink', () => {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
const MB = 1024 * 1024;
|
const BYTE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] as const;
|
||||||
const GB = MB * 1024;
|
|
||||||
|
|
||||||
export type ServiceState = 'live' | 'warn' | 'fail' | 'idle' | 'paused';
|
export type ServiceState = 'live' | 'warn' | 'fail' | 'idle' | 'paused';
|
||||||
|
|
||||||
@@ -8,15 +7,20 @@ export function formatBytes(value: number): string {
|
|||||||
return '0 B';
|
return '0 B';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value >= GB) {
|
if (value < 1024) {
|
||||||
return `${(value / GB).toFixed(2)} GB`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value >= MB) {
|
|
||||||
return `${(value / MB).toFixed(1)} MB`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${Math.round(value)} B`;
|
return `${Math.round(value)} B`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let unitIndex = 0;
|
||||||
|
let scaled = value;
|
||||||
|
|
||||||
|
while (scaled >= 1024 && unitIndex < BYTE_UNITS.length - 1) {
|
||||||
|
scaled /= 1024;
|
||||||
|
unitIndex += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const decimals = scaled >= 10 ? 1 : 2;
|
||||||
|
return `${scaled.toFixed(decimals)} ${BYTE_UNITS[unitIndex]}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildProxyLink(
|
export function buildProxyLink(
|
||||||
|
|||||||
Reference in New Issue
Block a user