feat: add stock pages and ticker navigation

Add stock search and detail pages with charts, dividends and shared event rows.

Use verified ticker links for portfolio names and logos, numeric dates and refreshed account cards.

Update the generated API contract.
This commit is contained in:
2026-09-10 21:23:41 +03:00
parent 0306526d33
commit 561222e468
56 changed files with 5051 additions and 190 deletions
+176 -1
View File
@@ -1,7 +1,7 @@
const ru = {
translation: {
appName: "WealthLedger",
navigation: { accounts: "Счета", settings: "Настройки" },
navigation: { accounts: "Счета", stocks: "Акции", settings: "Настройки" },
breadcrumbs: {
label: "Хлебные крошки",
accounts: "Брокерские счета",
@@ -11,6 +11,163 @@ const ru = {
events: "Будущие события",
settings: "Настройки",
},
stock: {
searchTitle: "Акции",
searchSubtitle:
"Найдите компанию, изучите цену и дивиденды, посмотрите свою историю владения.",
searchLabel: "Поиск акций",
searchPlaceholder: "Название или тикер, например Сбер или SBER",
searchHint: "Введите хотя бы два символа.",
searching: "Ищем акции",
noSearchResults:
"Акции не найдены. Попробуйте другое название или тикер.",
backToSearch: "Все акции",
searchVariants:
"Одна карточка на выпуск (ISIN). Обыкновенные и привилегированные акции показаны отдельно.",
apiUnavailable: "Торги через API недоступны",
apiAvailable: "Доступна через API",
exchangeUnknown: "Площадка не указана",
classCode: "Режим: {{code}}",
isin: "ISIN: {{value}}",
priceUnavailable: "Цена недоступна",
exchanges: {
REAL_EXCHANGE_MOEX: "Мосбиржа",
REAL_EXCHANGE_RTS: "СПБ Биржа",
REAL_EXCHANGE_OTC: "Внебиржевой рынок",
},
aboutCompany: "О компании",
priceTableHint: "Дневные закрытия · записей: {{count}} · сначала новые",
share: "Акция",
loading: "Загружаем страницу акции",
loadingSection: "Загружаем: {{section}}",
loadingPrice: "Загружаем текущую цену",
unavailable: "Акция недоступна",
ambiguousTicker:
"Этот тикер относится к нескольким выпускам. Выберите нужную акцию в поиске.",
unavailableText:
"Проверьте ссылку или попробуйте загрузить данные ещё раз.",
connect:
"Для просмотра акций подключите T-Банк в настройках или обновите подключение.",
sectionError: "Не удалось загрузить этот раздел. Попробуйте ещё раз.",
refreshPrice: "Обновить цену",
price: "Цена",
priceAt: "Цена на {{date}} UTC",
changeBase: "К закрытию предыдущего торгового дня, {{date}}",
noComparison:
"Изменение за день недоступно: нет подтверждённой цены сравнения.",
partialQuote: "Часть данных о цене или торгах временно недоступна.",
chart: "График цены",
period: "Период графика",
periods: {
month: "Месяц",
halfYear: "Полгода",
year: "Год",
all: "Вся история",
},
chartLabel: "Дневные цены акции, {{currency}}",
chartNote:
"Дневные цены по данным Т-Банка. Исторические цены могут учитывать корпоративные изменения.",
incomplete: "Последняя свеча ещё формируется: её цена может измениться.",
averageLine: "Текущая средняя покупка",
priceTable: "Цены в таблице",
date: "Дата",
candle: "Свеча",
complete: "Завершена",
forming: "Формируется",
noCandles:
"За этот период нет доступных цен. Возможно, торги ещё не начались.",
position: "Моя позиция",
quantity: "Количество акций",
value: "Текущая стоимость",
average: "Средняя покупка",
openReturn: "Результат позиции",
openReturnNote:
"Результат только открытой позиции: без проданных акций, дивидендов и полной доходности владения. «—» означает, что расчёт недоступен.",
portfolio: "Портфель",
snapshotAt: "Позиция на {{date}} UTC",
upcoming: "Предстоящие дивиденды",
companyEvent: "Событие компании",
perShare: "на акцию",
yourEstimate: "Оценка по вашей позиции",
record: "Дата реестра",
lastBuy: "Последний день покупки",
payment: "Выплата по данным источника",
noUpcoming: "Нет известных предстоящих выплат до {{to}}.",
dividendNote:
"Личная сумма — оценка по текущему количеству, не гарантия выплаты. Налоги не сопоставлены; дата выплаты компании может отличаться от дня зачисления на ваш счёт.",
estimateUnavailable:
"Данные позиции не удалось обновить. Личная оценка недоступна или рассчитана по сохранённой позиции.",
dividendHistory: "История дивидендов компании",
dividendWindow:
"События компании с {{from}} по {{to}}. Личные зачисления показаны отдельно в истории событий.",
noDividends: "Нет известных выплат за этот период.",
dateUnknown: "Дата не указана",
history: "Моя история событий",
historyNote:
"Операции по этой акции на доступных открытых счетах, включая периоды до текущего года. Дата сделки — дата операции брокера; количество — исполненная часть. История закрытых счетов не включена.",
eventFilter: "Фильтр событий",
filters: {
all: "Все",
trades: "Сделки",
dividends: "Дивиденды",
other: "Другие",
},
noOperations: "Нет доступных операций по выбранному фильтру.",
previous: "Назад",
next: "Далее",
eventQuantity: "{{value}} шт.",
eventPrice: "Цена: {{value}}",
company: "Компания и показатели",
sector: "Сектор",
lot: "Лот: {{value}} шт.",
partialCompany: "Часть сведений о компании временно недоступна.",
noFundamentals: "Нет доступных финансовых показателей.",
periodNote:
"TTM — последние 12 месяцев, MRQ — последний отчётный квартал. Дата отчётности не предоставлена источником. Отсутствующие значения не показаны.",
metrics: {
marketCapitalization: "Капитализация",
peRatioTtm: "P/E",
priceToBookTtm: "P/B",
revenueTtm: "Выручка",
ebitdaTtm: "EBITDA",
netIncomeTtm: "Чистая прибыль",
roe: "Рентабельность капитала (ROE)",
totalDebtMrq: "Общий долг",
netDebtToEbitda: "Чистый долг / EBITDA",
},
shareTypes: {
SHARE_TYPE_COMMON: "Обыкновенная акция",
SHARE_TYPE_PREFERRED: "Привилегированная акция",
SHARE_TYPE_ADR: "Депозитарная расписка",
SHARE_TYPE_GDR: "Депозитарная расписка",
},
sectors: {
financial: "Финансы",
energy: "Энергетика",
materials: "Материалы",
industrials: "Промышленность",
utilities: "Коммунальные услуги",
telecom: "Телекоммуникации",
it: "Информационные технологии",
consumer: "Потребительский сектор",
health_care: "Здравоохранение",
real_estate: "Недвижимость",
},
trading: {
unknown: "Статус торгов неизвестен",
SECURITY_TRADING_STATUS_NORMAL_TRADING: "Идут торги",
SECURITY_TRADING_STATUS_NOT_AVAILABLE_FOR_TRADING: "Торги недоступны",
SECURITY_TRADING_STATUS_OPENING_PERIOD: "Период открытия",
SECURITY_TRADING_STATUS_CLOSING_PERIOD: "Период закрытия",
SECURITY_TRADING_STATUS_BREAK_IN_TRADING: "Перерыв в торгах",
SECURITY_TRADING_STATUS_CLOSING_AUCTION: "Аукцион закрытия",
SECURITY_TRADING_STATUS_OPENING_AUCTION: "Аукцион открытия",
SECURITY_TRADING_STATUS_SESSION_OPEN: "Сессия открыта",
SECURITY_TRADING_STATUS_SESSION_CLOSE: "Сессия закрыта",
SECURITY_TRADING_STATUS_DEALER_NORMAL_TRADING: "Дилерские торги",
},
},
auth: {
login: "Вход",
register: "Регистрация",
@@ -33,6 +190,10 @@ const ru = {
scope: "Счёт",
allDescription: "Портфель по всем доступным инвестиционным счетам",
title: "Счета",
subtitle: "Ваши инвестиции по счетам",
currentValue: "Текущая стоимость",
stocks: "Акции",
bonds: "Облигации",
emptyTitle: "Нет инвестиционных счетов",
emptyText:
"Откройте инвестиционный счет в T-Bank, чтобы он появился здесь.",
@@ -120,6 +281,20 @@ const ru = {
share: "Доля: {{value}}",
},
ledger: {
occurredAt: "{{date}} UTC",
quantity: "{{value}} шт.",
price: "Цена: {{value}}",
operationType: {
buy: "Покупка",
sell: "Продажа",
dividend: "Зачисление дивидендов",
tax: "Налог",
fee: "Комиссия",
input: "Зачисление ценных бумаг",
output: "Вывод ценных бумаг",
transfer: "Перевод между счетами",
other: "Другая операция",
},
title: "Журнал операций",
back: "К счету",
open: "Открыть журнал",