import type { FC } from "react"; import { Alert, Button, Group, Skeleton, Stack, Text, } from "@mantine/core"; import { LoadingSkeleton } from "@wealthledger/ui"; import { useTranslation } from "react-i18next"; import { instrumentDetailsStyles, InstrumentHeader, InstrumentMetric, InstrumentQuoteStatus, InstrumentChart as StockChart, InstrumentEvents as StockEvents, InstrumentPosition as StockPosition, InstrumentSection as StockSection, } from "@/features/instrument-details"; import { formatDate, formatDecimal, formatMoney, } from "@/shared/lib"; 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 = (props) => { const { vm } = props; const { t } = useTranslation(); const stock = vm.stock; if (!stock) return; const subtitle = ( <> {stock.ticker} ·{" "} {t(`stock.shareTypes.${stock.shareType}`, { defaultValue: t("stock.share"), })} ); const appearanceClass = vm.quote?.change?.value.startsWith("-") ? classes.negative : classes.positive; let quoteContent; if (vm.quoteState.loading) { quoteContent = ( ); } else { quoteContent = (
{formatMoney(vm.quote?.price)} {!!vm.quote?.change && ( {vm.quote.change.value.startsWith("-") ? "" : "+"} {formatMoney(vm.quote.change)} {vm.quote.changePercent === null ? "" : ` (${formatDecimal(vm.quote.changePercent)} %)`} )} {vm.quote?.previousCloseDate ? ( {t("stock.changeBase", { date: formatDate(vm.quote.previousCloseDate), })} ) : ( {t("stock.noComparison")} )} {!!vm.quote?.unavailable.length && ( {t("stock.partialQuote")} )} {!!vm.quoteState.refreshing && ( {t("accounts.refreshing")} )}
); } let companyMetricsContent; if (vm.company) { if (vm.company.metrics.length > 0) { companyMetricsContent = (
{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"))}`; return ( ); })}
); } else { companyMetricsContent = {t("stock.noFundamentals")}; } } return ( {!!vm.company?.description && ( {vm.company.description} )} {!!(vm.refreshError || vm.stockState.error || vm.quoteState.error) && ( {t(vm.quote ? "accounts.refreshError" : "stock.sectionError")} )} {quoteContent} {!!stock.sector && ( {t("stock.sector")}:{" "} {t(`stock.sectors.${stock.sector}`, { defaultValue: stock.sector, })} )} {stock.lot !== null && {t("stock.lot", { value: stock.lot })}} {!!vm.company && ( {vm.company.unavailable.length > 0 && ( {t("stock.partialCompany")} )} {companyMetricsContent} {t("stock.periodNote")} )} ); }; function metricUnit( metric: NonNullable["metrics"][number], unknownCurrency: string, ) { if (metric.unit === "money") return metric.currency === "RUB" ? "₽" : (metric.currency ?? unknownCurrency); return metric.unit === "percent" ? "%" : "×"; } export { StockPageView };