feat(storage): functional Storage dashboard (§A6)
Replace the "coming soon" stub with a real dashboard wired to `GET /storage`. modern-sk visuals: a layered disk-capacity gauge (library share vs other-used vs free), stat tiles (tracks/artists/albums/playtime/ footprint/avg size), per-format size bars, metadata-health badges, source breakdown, a popularity-weighted top-genres cloud, and playful fun facts. - types: full `StorageStats` shape + `toStorageStats` snake→camel mapper - endpoint: re-point `getStorageStats` to `GET /storage` with transform - lib: `formatLongDuration` for big playtime spans - i18n: `storage.*` keys (en + ru) - three list states (loading / error / empty) per the UI invariant Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,20 @@ export function formatDateTime(iso: string | undefined): string | undefined {
|
||||
}).format(d);
|
||||
}
|
||||
|
||||
/** Human "X days Y hours" style for big spans (e.g. total library playtime).
|
||||
* Shows the two most-significant non-zero units; falls back to "0m". */
|
||||
export function formatLongDuration(seconds: number): string {
|
||||
if (seconds <= 0) return '0m';
|
||||
const days = Math.floor(seconds / 86400);
|
||||
const hours = Math.floor((seconds % 86400) / 3600);
|
||||
const mins = Math.floor((seconds % 3600) / 60);
|
||||
const parts: string[] = [];
|
||||
if (days) parts.push(`${days}d`);
|
||||
if (hours) parts.push(`${hours}h`);
|
||||
if (mins && parts.length < 2) parts.push(`${mins}m`);
|
||||
return parts.slice(0, 2).join(' ') || '0m';
|
||||
}
|
||||
|
||||
export function formatCount(n: number): string {
|
||||
if (n < 1000) return String(n);
|
||||
if (n < 1_000_000) return `${(n / 1000).toFixed(1)}K`;
|
||||
|
||||
Reference in New Issue
Block a user