feat: redesign portfolio position cards

This commit is contained in:
2026-08-17 13:35:22 +03:00
parent da55de5b50
commit 5168870cf2
9 changed files with 182 additions and 44 deletions
+1
View File
@@ -2,6 +2,7 @@ export type { Portfolio } from "./api/usePortfolio";
export { usePortfolio } from "./api/usePortfolio";
export {
formatMoney,
formatShare,
formatSnapshotTime,
portfolioErrorCode,
} from "./model/display";
+24 -1
View File
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { formatMoney } from "./display";
import { formatMoney, formatShare } from "./display";
describe("portfolio display helpers", () => {
it("formats decimal strings without financial arithmetic", () => {
@@ -10,4 +10,27 @@ describe("portfolio display helpers", () => {
expect(formatMoney({ value: "0.5", currency: "USD" })).toBe("0,5 USD");
expect(formatMoney(null)).toBe("—");
});
it("formats a share as a display-only percentage", () => {
expect(
formatShare(
{ currency: "RUB", value: "150" },
{ currency: "RUB", value: "1000" },
),
).toBe("15 %");
expect(
formatShare(
{ currency: "RUB", value: "123.45" },
{ currency: "RUB", value: "999.99" },
),
).toBe("12,3 %");
expect(formatShare(null, { currency: "RUB", value: "100" })).toBeNull();
expect(formatShare({ currency: "RUB", value: "100" }, null)).toBeNull();
expect(
formatShare(
{ currency: "RUB", value: "100" },
{ currency: "RUB", value: "0" },
),
).toBeNull();
});
});
+15
View File
@@ -17,6 +17,21 @@ export function formatMoney(money: Money | null): string {
return `${formatDecimal(money.value)} ${money.currency === "RUB" ? "₽" : money.currency}`;
}
export function formatShare(
numerator: Money | null,
denominator: Money | null,
): string | null {
if (!numerator || !denominator) return null;
const total = Number(denominator.value);
if (!Number.isFinite(total) || total <= 0) return null;
const percent = (Number(numerator.value) / total) * 100;
if (!Number.isFinite(percent)) return null;
const rounded = new Intl.NumberFormat("ru-RU", {
maximumFractionDigits: 1,
}).format(percent);
return `${rounded} %`;
}
export function formatSnapshotTime(value: string): string {
return new Intl.DateTimeFormat("ru-RU", {
dateStyle: "medium",