mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 14:40:52 +00:00
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:
1 parent
67cc56f707
commit
56daa57350
12 files changed
+84
-70
No files matched your search
@@ -11,7 +11,6 @@ import {
|
||||
documentCardKeyAction,
|
||||
documentDateKey,
|
||||
expandRangeSelection,
|
||||
formatBytes,
|
||||
formatMonthLabel,
|
||||
hasProcessingDocument,
|
||||
isDocumentProcessing,
|
||||
@@ -216,15 +215,6 @@ describe("timeline items", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("byte formatting", () => {
|
||||
it("scales units and keeps one decimal below 10", () => {
|
||||
expect(formatBytes(0, "en")).toBe("0 B");
|
||||
expect(formatBytes(512, "en")).toBe("512 B");
|
||||
expect(formatBytes(26_214_400, "en")).toBe("25 MB");
|
||||
expect(formatBytes(1_288_490_189, "en")).toBe("1.2 GB");
|
||||
});
|
||||
});
|
||||
|
||||
describe("upload response contract (§3.2)", () => {
|
||||
it("HTTP 200 + meta.duplicate returns the EXISTING row as a duplicate", () => {
|
||||
const body = JSON.stringify({
|
||||
|
||||
@@ -31,11 +31,8 @@ import { cn } from "@/lib/utils";
|
||||
import type { InboundDocumentDto } from "@/lib/validations/inbound-documents";
|
||||
import { DOCUMENT_KIND_ICONS } from "./document-kind-meta";
|
||||
import type { UploadQueueItem } from "./use-document-upload";
|
||||
import {
|
||||
documentCardKeyAction,
|
||||
formatBytes,
|
||||
isDocumentProcessing,
|
||||
} from "./vault-utils";
|
||||
import { formatBytes } from "@/lib/format/bytes";
|
||||
import { documentCardKeyAction, isDocumentProcessing } from "./vault-utils";
|
||||
|
||||
/** How long the "Ready" chip stays up after a card's index lands. */
|
||||
const READY_FLASH_MS = 3000;
|
||||
|
||||
@@ -102,7 +102,7 @@ import {
|
||||
useDocumentSummary,
|
||||
useSuggestDetails,
|
||||
} from "./use-document-assist";
|
||||
import { formatBytes } from "./vault-utils";
|
||||
import { formatBytes } from "@/lib/format/bytes";
|
||||
import { QueryErrorRow } from "@/components/ui/query-error-row";
|
||||
|
||||
type PatchInput = {
|
||||
|
||||
@@ -75,7 +75,7 @@ import type { InboundDocumentKindValue } from "@/lib/validations/inbound-documen
|
||||
import { WrittenOutcomeLine } from "@/components/outcome/written-outcome-line";
|
||||
import type { WrittenOutcome } from "@/lib/outcome/written-outcome";
|
||||
|
||||
import { formatBytes } from "../vault-utils";
|
||||
import { formatBytes } from "@/lib/format/bytes";
|
||||
import {
|
||||
errorCodeOf,
|
||||
isStoppingError,
|
||||
|
||||
@@ -19,7 +19,7 @@ import { type RefObject } from "react";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import type { DocumentUsageDto } from "@/lib/validations/inbound-documents";
|
||||
import { formatBytes } from "./vault-utils";
|
||||
import { formatBytes } from "@/lib/format/bytes";
|
||||
|
||||
/** Usage fraction above which the quota bar appears. */
|
||||
const QUOTA_BAR_THRESHOLD = 0.8;
|
||||
|
||||
@@ -302,21 +302,6 @@ export function formatMonthLabel(key: string, locale: string): string {
|
||||
}).format(new Date(`${key}-15T12:00:00.000Z`));
|
||||
}
|
||||
|
||||
/** Locale byte formatting: 0 B / 412 KB / 1.2 MB / 2.4 GB. */
|
||||
export function formatBytes(bytes: number, locale: string): string {
|
||||
const units = ["B", "KB", "MB", "GB"] as const;
|
||||
let value = Math.max(0, bytes);
|
||||
let unit = 0;
|
||||
while (value >= 1024 && unit < units.length - 1) {
|
||||
value /= 1024;
|
||||
unit += 1;
|
||||
}
|
||||
const digits = unit === 0 || value >= 100 ? 0 : value >= 10 ? 1 : 1;
|
||||
return `${new Intl.NumberFormat(locale, {
|
||||
maximumFractionDigits: digits,
|
||||
}).format(value)} ${units[unit]}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shift-click range selection over the timeline's visual order. Selects
|
||||
* every id between the last plainly-toggled anchor and the target
|
||||
|
||||
Reference in new issue
Block a user