refactor: tighten types and reuse instrument UI
Frontend verify / commits (pull_request) Successful in 24s
Frontend verify / security (pull_request) Failing after 9s
Frontend verify / verify (pull_request) Skipped

This commit is contained in:
2026-09-29 06:15:31 +03:00
parent b404c1802a
commit 7c28a423dd
67 changed files with 380 additions and 347 deletions
@@ -1,39 +1,11 @@
.page {
max-width: 1120px;
margin: 0 auto;
min-width: 0;
}
.price {
font-size: clamp(2rem, 4vw, 3.2rem);
font-weight: 700;
letter-spacing: -0.04em;
font-variant-numeric: tabular-nums;
line-height: 1.2;
overflow-wrap: anywhere;
}
.positive {
color: var(--mantine-color-green-8);
color: var(--wl-color-positive);
}
.negative {
color: var(--mantine-color-red-8);
color: var(--wl-color-negative);
}
.dividend {
padding: var(--mantine-spacing-md);
background: var(--mantine-color-green-0);
border-radius: var(--mantine-radius-md);
}
.companyMetrics {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(135px, 1fr));
gap: var(--mantine-spacing-lg);
}
.companyMetrics > div {
padding: var(--mantine-spacing-sm);
background: var(--wl-color-subtle);
border-radius: var(--mantine-radius-md);
}
@media (max-width: 48em) {
.companyMetrics {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
+26 -36
View File
@@ -2,7 +2,6 @@ import type { FC } from "react";
import {
Alert,
Badge,
Button,
Group,
Skeleton,
@@ -13,7 +12,10 @@ import { LoadingSkeleton } from "@wealthledger/ui";
import { useTranslation } from "react-i18next";
import {
instrumentDetailsStyles,
InstrumentHeader,
InstrumentMetric,
InstrumentQuoteStatus,
InstrumentChart as StockChart,
InstrumentEvents as StockEvents,
InstrumentPosition as StockPosition,
@@ -23,7 +25,6 @@ import {
formatDate,
formatDecimal,
formatMoney,
formatSnapshotTime,
} from "@/shared/lib";
import type { StockPageVm } from "./StockPage.vm";
@@ -31,6 +32,16 @@ import type { StockPageVm } from "./StockPage.vm";
import { StockDividends } from "./components/StockDividends.view";
import classes from "./StockPage.module.css";
const compactMetricFormatter = new Intl.NumberFormat("ru-RU", {
maximumFractionDigits: 2,
notation: "compact",
});
const metricFormatter = new Intl.NumberFormat("ru-RU", {
maximumFractionDigits: 2,
notation: "standard",
});
type StockPageViewProps = { vm: StockPageVm };
const StockPageView: FC<StockPageViewProps> = (props) => {
@@ -64,13 +75,13 @@ const StockPageView: FC<StockPageViewProps> = (props) => {
else {
quoteContent = (
<div aria-live="polite">
<Text className={classes.price} mt="lg">
<Text className={instrumentDetailsStyles.price} mt="lg">
{formatMoney(vm.quote?.price)}
</Text>
{!!vm.quote?.change && (
<Text className={appearanceClass} fw={600}>
{vm.quote?.change?.value.startsWith("-") ? "" : "+"}
{vm.quote.change.value.startsWith("-") ? "" : "+"}
{formatMoney(vm.quote.change)}
@@ -95,19 +106,7 @@ const StockPageView: FC<StockPageViewProps> = (props) => {
)}
<Group gap="sm" mt="sm">
<Badge color="gray" variant="light">
{t(`stock.trading.${vm.quote?.tradingStatus}`, {
defaultValue: t("stock.trading.unknown"),
})}
</Badge>
{!!vm.quote?.priceAt && (
<Text c="dimmed" size="xs">
{t("stock.priceAt", {
date: formatSnapshotTime(vm.quote.priceAt),
})}
</Text>
)}
<InstrumentQuoteStatus quote={vm.quote} />
</Group>
{!!vm.quote?.unavailable.length && (
@@ -130,25 +129,16 @@ const StockPageView: FC<StockPageViewProps> = (props) => {
if (vm.company) {
if (vm.company.metrics.length > 0) {
companyMetricsContent = (
<div className={classes.companyMetrics}>
{vm.company.metrics.map(metric => (
<div key={metric.key}>
<Text c="dimmed" size="xs">
{t(`stock.metrics.${metric.key}`)}
<div className={instrumentDetailsStyles.metricsGrid}>
{vm.company.metrics.map((metric) => {
const label = [t(`stock.metrics.${metric.key}`), metric.period].filter(Boolean).join(" · ");
const formatter = metric.unit === "money" ? compactMetricFormatter : metricFormatter;
const value = `${formatter.format(metric.value)} ${metricUnit(metric, t("stock.currencyUnknown"))}`;
{metric.period ? ` · ${metric.period}` : ""}
</Text>
<Text fw={600} size="lg">
{new Intl.NumberFormat("ru-RU", {
maximumFractionDigits: 2,
notation:
metric.unit === "money" ? "compact" : "standard",
}).format(metric.value)}{" "}
{metricUnit(metric, t("stock.currencyUnknown"))}
</Text>
</div>
))}
return (
<InstrumentMetric key={metric.key} label={label} value={value} />
);
})}
</div>
);
}
@@ -158,7 +148,7 @@ const StockPageView: FC<StockPageViewProps> = (props) => {
}
return (
<Stack className={classes.page} gap="lg">
<Stack className={instrumentDetailsStyles.page} gap="lg">
<InstrumentHeader
logoSrc={vm.logoSrc}
name={stock.name}