refactor(format): one byte formatter for every surface

The document vault and the admin area each carried a formatBytes; the
same backup read 1.25 MB in one and 1.2 MB in the other, and only the
vault knew gigabytes. The vault rule (one decimal below 100, up to GB,
digits in the reader's locale) now lives in src/lib/format/bytes.ts and
takes the locale, so the server-rendered share page uses it too. A test
holds the definition to that one module.
This commit is contained in:
Marc-André Bombeck committed 2026-10-20 12:55:06 +02:00
1 parent 67cc56f707
commit 56daa57350
12 files changed
+84 -70

No files matched your search

+3 -3
View File
@@ -843,7 +843,7 @@ async function uploadOnce(file: File) {
}
export function BackupsSection() {
const { t } = useTranslations();
const { t, locale } = useTranslations();
const fmt = useFormatters();
const queryClient = useQueryClient();
@@ -1327,7 +1327,7 @@ export function BackupsSection() {
</Badge>
</td>
<td className="px-3 py-2 text-right font-mono text-xs tabular-nums">
{formatBytes(row.sizeBytes, fmt)}
{formatBytes(row.sizeBytes, locale)}
</td>
<td className="text-muted-foreground px-3 py-2 text-right text-xs whitespace-nowrap">
{fmt.dateTime(row.createdAt)}
@@ -1394,7 +1394,7 @@ export function BackupsSection() {
</div>
<p className="text-muted-foreground text-xs">
{t("admin.section.backups.colSize")}:{" "}
{formatBytes(row.sizeBytes, fmt)} ·{" "}
{formatBytes(row.sizeBytes, locale)} ·{" "}
{fmt.dateTime(row.createdAt)}
</p>
<div className="mt-2 flex flex-wrap items-center gap-2">
@@ -45,20 +45,8 @@ const ENDPOINT = "/api/admin/maintenance/measurements";
/** How often the card asks while a run is waiting or working. */
const POLL_MS = 10_000;
const GIB = 1024 * 1024 * 1024;
function sizeLabel(
bytes: number,
fmt: ReturnType<typeof useFormatters>,
): string {
// The shared formatter stops at MB; this table is routinely gigabytes.
return bytes >= GIB
? `${fmt.number(bytes / GIB, 2)} GB`
: formatBytes(bytes, fmt);
}
export function MeasurementMaintenanceCard() {
const { t } = useTranslations();
const { t, locale } = useTranslations();
const fmt = useFormatters();
const queryClient = useQueryClient();
@@ -132,11 +120,11 @@ export function MeasurementMaintenanceCard() {
<div className="grid gap-3 sm:grid-cols-2">
<StatTile
label={t("admin.section.maintenance.tableSize")}
value={sizeLabel(s.sizes.tableBytes, fmt)}
value={formatBytes(s.sizes.tableBytes, locale)}
/>
<StatTile
label={t("admin.section.maintenance.indexSize")}
value={sizeLabel(s.sizes.indexBytes, fmt)}
value={formatBytes(s.sizes.indexBytes, locale)}
/>
</div>
) : null}
@@ -84,7 +84,7 @@ function freshnessLabel(
}
function AccountRow({ row }: { row: OffhostAccountRow }) {
const { t } = useTranslations();
const { t, locale } = useTranslations();
const fmt = useFormatters();
return (
@@ -128,7 +128,7 @@ function AccountRow({ row }: { row: OffhostAccountRow }) {
})
: `${fmt.dateTime(row.lastSuccessAt)} · ${formatBytes(
row.sizeBytes,
fmt,
locale,
)} · ${t("admin.section.backups.offhost.age", {
hours: row.ageHours ?? 0,
})}`}