merge task/t2: money formatting + hide-amounts

# Conflicts:
#	web/vitest.setup.ts
This commit is contained in:
Munkherdene 2026-08-22 20:12:26 +08:00
commit 1051ecdc5b
2 changed files with 37 additions and 0 deletions

14
src/ds/money.test.ts Normal file
View file

@ -0,0 +1,14 @@
import { describe, it, expect, beforeEach } from "vitest";
import { tugrik, tugrikShort, grouped, tugrikRaw, isHidden, setHidden, MASKED } from "./money";
beforeEach(() => { try { localStorage.clear(); } catch {} });
describe("money", () => {
it("groups tugrik", () => expect(tugrik("452200")).toBe("452,200₮"));
it("groups digits only", () => expect(grouped("500000")).toBe("500,000"));
it("abbreviates millions", () => expect(tugrikShort("1200000")).toBe("1,2сая₮"));
it("keeps sub-million as full", () => expect(tugrikShort("900000")).toBe("900,000₮"));
it("masks when hidden", () => { setHidden(true); expect(tugrik("100")).toBe(MASKED); });
it("raw never masks", () => { setHidden(true); expect(tugrikRaw("100")).toBe("100₮"); });
it("isHidden reflects storage", () => { setHidden(true); expect(isHidden()).toBe(true); });
});

23
src/ds/money.ts Normal file
View file

@ -0,0 +1,23 @@
export const MASKED = "••••••";
const KEY = "mercury.hideAmounts";
export function isHidden(): boolean {
try { return localStorage.getItem(KEY) === "true"; } catch { return false; }
}
export function setHidden(b: boolean): void {
try { localStorage.setItem(KEY, String(b)); } catch {}
}
function num(v: string | number): number {
return typeof v === "number" ? v : parseFloat(v || "0") || 0;
}
export function grouped(v: string | number): string {
return new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 }).format(num(v));
}
export function tugrikRaw(v: string | number): string { return `${grouped(v)}`; }
export function tugrikShortRaw(v: string | number): string {
const m = num(v) / 1_000_000;
if (m >= 1) return `${m.toFixed(1).replace(".", ",")}сая₮`;
return tugrikRaw(v);
}
export function tugrik(v: string | number): string { return isHidden() ? MASKED : tugrikRaw(v); }
export function tugrikShort(v: string | number): string { return isHidden() ? MASKED : tugrikShortRaw(v); }