feat(driver-pwa): pure money/phone/amount helpers + tests
Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { keypadReduce, withCommission } from "./amount";
|
||||||
|
|
||||||
|
describe("amount keypad", () => {
|
||||||
|
it("appends digits, ignores leading zero, caps length", () => {
|
||||||
|
expect(keypadReduce("0", "5")).toBe("5");
|
||||||
|
expect(keypadReduce("100", "0")).toBe("1000");
|
||||||
|
expect(keypadReduce("", "0")).toBe("");
|
||||||
|
expect(keypadReduce("999999", "9")).toBe("999999");
|
||||||
|
});
|
||||||
|
it("backspace removes last digit", () => {
|
||||||
|
expect(keypadReduce("105", "back")).toBe("10");
|
||||||
|
expect(keypadReduce("", "back")).toBe("");
|
||||||
|
});
|
||||||
|
it("withCommission computes total at given rate", () => {
|
||||||
|
expect(withCommission(1000, 0.052)).toEqual({ commission: 52, total: 1052 });
|
||||||
|
expect(withCommission(333, 0.052)).toEqual({ commission: 17.32, total: 350.32 });
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
const MAX_DIGITS = 6;
|
||||||
|
|
||||||
|
export function keypadReduce(current: string, key: string): string {
|
||||||
|
if (key === "back") return current.slice(0, -1);
|
||||||
|
if (!/^\d$/.test(key)) return current;
|
||||||
|
if (current === "0") return key === "0" ? "0" : key; // replace leading zero
|
||||||
|
if (current === "" && key === "0") return ""; // no leading zero from empty
|
||||||
|
if (current.length >= MAX_DIGITS) return current;
|
||||||
|
return current + key;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function withCommission(base: number, rate: number): { commission: number; total: number } {
|
||||||
|
const commission = Math.round(base * rate * 100) / 100;
|
||||||
|
const total = Math.round((base + commission) * 100) / 100;
|
||||||
|
return { commission, total };
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { formatMoney, formatPhone, digitsOnly } from "./format";
|
||||||
|
|
||||||
|
describe("format", () => {
|
||||||
|
it("formatMoney groups thousands and adds ₽", () => {
|
||||||
|
expect(formatMoney(1000)).toBe("1 000 ₽");
|
||||||
|
expect(formatMoney(-3200)).toBe("−3 200 ₽");
|
||||||
|
expect(formatMoney(0)).toBe("0 ₽");
|
||||||
|
});
|
||||||
|
it("digitsOnly strips non-digits", () => {
|
||||||
|
expect(digitsOnly("+7 (914) 305-44-00")).toBe("79143054400");
|
||||||
|
});
|
||||||
|
it("formatPhone renders +7 mask progressively", () => {
|
||||||
|
expect(formatPhone("9143054400")).toBe("+7 914 305-44-00");
|
||||||
|
expect(formatPhone("914305")).toBe("+7 914 305");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
export function digitsOnly(s: string): string {
|
||||||
|
return (s || "").replace(/\D/g, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatMoney(rub: number): string {
|
||||||
|
const neg = rub < 0;
|
||||||
|
const abs = Math.abs(Math.round(rub));
|
||||||
|
const grouped = abs.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " ");
|
||||||
|
return `${neg ? "−" : ""}${grouped} ₽`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Render a Russian mobile from up-to-10 national digits as +7 914 305-44-00. */
|
||||||
|
export function formatPhone(national10: string): string {
|
||||||
|
const d = digitsOnly(national10).slice(0, 10);
|
||||||
|
const p: string[] = ["+7"];
|
||||||
|
if (d.length > 0) p.push(" " + d.slice(0, 3));
|
||||||
|
if (d.length > 3) p.push(" " + d.slice(3, 6));
|
||||||
|
if (d.length > 6) p.push("-" + d.slice(6, 8));
|
||||||
|
if (d.length > 8) p.push("-" + d.slice(8, 10));
|
||||||
|
return p.join("");
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user