Commit Graph
26 Commits
Author SHA1 Message Date
Dmitry 2645e56197 feat(app): календарь выплат сеткой по месяцам
Месячная сетка с дивидендами, купонами, амортизациями и погашениями по дням, выплаченными и ожидаемыми, с сводкой за месяц и списком выплат выбранного дня. Раскладка без прокрутки страницы: высота ячеек подстраивается под окно, на широком экране список справа. Прежний список остался вторым режимом.
2026-09-20 10:46:38 +03:00
Dmitry 6891028074 fix(app): подписи осей и легенда графика стоимости
Крайние подписи оси Y (414.6K поверх 400K) скрыты, числа в русской краткой форме, подписи оси X не обрезаются справа. Добавлена легенда: сплошная линия — стоимость, пунктир — вложено нетто.
2026-09-20 10:46:35 +03:00
Dmitry 617682351b feat(app): график цены — периоды, режимы, бенчмарки и мои сделки
Переключатель периода (7д…все и свой диапазон), режимы «Цена ₽ / ₽ + НКД / %», наложение бенчмарков в процентах (у ценового индекса пометка «без дивидендов»), изменение за период, заливка, подписи min/max, курсор с подсказкой и точки покупок и продаж. Ось X по датам, подписи осей без наложений.
2026-09-20 10:46:33 +03:00
Dmitry 322c60a359 style(app): остальные экраны под новый визуальный язык и форматирование
Доходы, ребалансировка, налоги, импорт, цели, здоровье данных, правила, транзакции, категории, cashflow, pending: новые виджеты и токены темы, dart format. Тесты подстроены под новые модели.
2026-09-19 22:14:27 +03:00
Dmitry 62d36aa3e8 feat(app): новый визуальный язык, верхняя навигация, портфели, создание счетов и ручные события
Тема и общие виджеты (AssetIcon, ServiceMark, HelpTip, глоссарий), верхняя панель TopNav и вкладки Аналитики вместо NavSidebar, иконки PWA. Экраны: портфели, создание брокерского счёта, ручное событие, правка инструмента, Обзор карточками по скоупам и таблица активов, пересчёт метрик с опросом /metrics/status. design-system.md обновлён.
2026-09-19 22:14:21 +03:00
Dmitry a559d6de3e feat(app): смена сервера API и очистка данных устройства при выходе
Адрес бэкенда хранится в shared_preferences и читается до runApp; switchApiBaseUrl сбрасывает токены и кэш старого сервера. Выход стирает refresh-токен и sqlite-кэш ответов. Экран настроек переделан под разделы.
2026-09-19 22:14:14 +03:00
Dmitry bfe74ca47c chore(app): перегенерировать Dart-клиент
Портфели, создание счетов, ручные события, PATCH инструмента, ScopeCardOut, MetricsStatusOut, логотипы в HoldingOut.
2026-09-19 22:14:07 +03:00
Dmitry e4a64cd1df feat(app): редизайн Обзора под новый визуальный язык
Локальные _StatTile/_ChartCard заменены на общие StatTile/SectionCard,
блоки капитала/месяца/инвестиций получили SectionHeader, ряды плиток
переведены с Wrap на TileCarousel. Заодно цвет «ок» в чипе качества
данных переведён с хардкод Colors.green на ChartColors.slot3Aqua —
тот же цвет, что signColor уже использует для положительного знака.
2026-09-19 15:31:47 +03:00
Dmitry 8d404ce7d3 feat(app): группированный сайдбар навигации на широком breakpoint
NavSidebar (>=1200): пункты меню сгруппированы под капс-заголовками,
у активного пункта левая акцентная полоска, у «Здоровье» — счётчик
замечаний качества данных из dataQualityProvider, внизу закреплён
профиль (аватар-инициал + email из meProvider). Список направлений
вынесен в nav_destinations.dart — общий для app_shell.dart и
nav_sidebar.dart. На 600–1200 остаётся свёрнутый NavigationRail: в
иконку-без-подписи заголовки групп и профиль всё равно не помещаются.

NavSidebar смонтирован всё время сессии и держит dataQualityProvider
и meProvider живыми — раньше эти запросы уходили только при открытии
Обзора/Здоровья/Настроек.
2026-09-19 15:31:37 +03:00
Dmitry 9d49a9539b feat(app): SectionHeader и TileCarousel — общие виджеты редизайна
SectionHeader — заголовок блока с короткой акцентной полоской вместо
разделителя. TileCarousel — горизонтальная прокрутка StatTile вместо
Wrap, не требует общей высоты у детей. Оба без экрана-потребителя пока
не влияют на вид приложения.
2026-09-19 15:31:26 +03:00
Dmitry bea239ba2d feat(app): токены темы — тёмный сине-индиго фон, единый синий акцент
ColorScheme в AppTheme.light()/.dark() строится из одного seed вместо
двух параллельных наборов чисел для тёмной и светлой темы — акцент
сведён к ChartColors.slot1Blue, чтобы фирменный цвет интерфейса и
основной цвет графиков совпадали. cardTheme/navigationRailTheme/
navigationBarTheme заданы централизованно и каскадом меняют вид
существующих карточек и навигации на всех экранах.
2026-09-19 15:31:16 +03:00
Dmitry 53da374c5a Merge: offline-кэш на остальных экранах 2026-09-19 14:00:24 +03:00
Dmitry 7b419f4188 feat(app): offline-кэш на остальных экранах — фаза 5
accounts, cashflow, categories, goals, income, portfolio (+instrument),
rebalance, tax, rules переведены на Cached<T> по контракту
docs/ai/offline-cache.md. Общие/второстепенные селекторы (scopesProvider,
categoriesListProvider и т.п.) оставлены как есть — не основной контент
экрана. sync_page.dart и settings_page.dart не тронуты (первый — заменён
health-page отдельно, второй — чистые действия без списка для баннера).
2026-09-19 14:00:13 +03:00
Dmitry 4790863263 Merge: Flutter CI-джоба + сборка Linux/Windows 2026-09-19 13:07:23 +03:00
Dmitry e1fcef6364 ci(app): Flutter CI-джоба + сборка Linux/Windows
analyze+test и flutter build linux/windows в отдельных джобах по образцу
backend. Линуксовая сборка требует libsecret-1-dev/pkg-config (apt) и
подавления -Werror=deprecated-literal-operator в CMake — flutter_secure_storage_linux
вендорит старый nlohmann/json.hpp, который на новых компиляторах иначе
не собирается.
2026-09-19 13:04:40 +03:00
Dmitry 68e1e53535 feat(app): страница здоровья вместо разрозненных /sync и data quality
/sync и блок data quality на дашборде сведены в один /health с двумя
вкладками (Источники, Качество данных); дашборд ссылается на неё вместо
собственного bottom sheet. Список находок вынесен в общий виджет
DataQualityList, чтобы не дублировать рендер.
2026-09-19 12:59:27 +03:00
Dmitry ffc5ed959a feat(app): offline-кэш GET-запросов на дашборде — фаза 5
CacheInterceptor кэширует каждый успешный GET по путь+параметры в drift
(sqlite нативно, wasm+OPFS в браузере) и подменяет им сетевую ошибку;
провайдер отдаёт Cached<T>, экран показывает баннер «данные на …».
Контракт для остальных экранов — docs/ai/offline-cache.md.

flake.nix: libsecret/pkg-config для линуксовой сборки, jq/curl для
just app-web-assets (сборка sqlite3.wasm + drift_worker.js).
2026-09-19 12:40:08 +03:00
Dmitry df49d99af4 fix(app): дата без времени парсится как UTC, а не в локальной зоне
DateTime.tryParse("2026-09-18") в Dart возвращает локальное время, не UTC —
на MSK (UTC+3) это трёхчасовой сдвиг относительно того, что сервер имел
в виду датой без времени. asDate() перетэговывает те же значения полей как
UTC вместо доверия tryParse; настоящий таймстемп с "Z"/смещением проходит
насквозь без изменений.
2026-09-19 10:56:42 +03:00
Dmitry baac83149f fix(api): Decimal-параметр ребалансировки ломал генерацию Dart-клиента
cash_available был bare Decimal | None в Query(...) — pydantic отдаёт для
Decimal anyOf[number, pattern-string], который openapi-generator не умеет
свернуть в один тип и вместо этого синтезирует пустой класс CashAvailable
без полей (синтаксически битый .dart, компиляция всего пакета падает).
Остальные Decimal-поля в API давно оборачиваются в Money/MoneyOpt
(api/schemas/common.py) ровно за счёт этого — anyOf[string, null] генератор
понимает. cash_available обошёл соглашение, потому что это единственный
Decimal-параметр запроса во всём API, а не тело ответа.
2026-09-19 10:56:33 +03:00
Dmitry ccd06f32f7 chore(app): сгенерированный Dart-клиент
just gen-client — покрывает links, cashflow-broker, ручные цены,
imports/pending, goals/income/rebalance/tax/benchmarks.
2026-09-19 10:46:09 +03:00
Dmitry b69bb4a0c9 feat(app): экраны импорта отчётов, целей, доходов, ребалансировки, налогов и бенчмарков
Импорт (/imports, /instruments/pending) и фаза 4 (/goals, /income,
/rebalance, /tax, аналитика-хаб с benchmarks_card) — по
docs/ai/import-contract.md и docs/ai/phase4-contract.md. file_picker для
загрузки отчёта.
2026-09-19 10:44:38 +03:00
Dmitry c588b575a7 feat(app): экран «События»
Лента брокерского леджера: фильтры по счёту, типу, датам и поиск, бесконечная
прокрутка с явной кнопкой «Ещё», карточка события в bottom sheet с переходом на
инструмент. Паттерн контроллера и пагинации взят у транзакций один в один — это тот
же список с фильтрами, и второй способ делать одно и то же был бы просто вторым
способом его чинить.

Пункт навигации стоит между «Портфелем» и «Потоками», а не рядом с «Операциями»:
это инвестиционная лента, а в операциях лежит ZenMoney.

eventKindLabels переиспользован из portfolio/labels.dart, где он уже жил ради карточки
инструмента, а не скопирован: два словаря подписей для одного енума разъезжаются на
первом же новом типе события.

Бэкенд не менялся — /events с фильтрами и пагинацией закрывает экран целиком,
клиент не перегенерировался.
2026-09-18 15:08:12 +03:00
Dmitry 1d7769ffbf feat(app): экраны портфеля — позиции, аллокация и карточка инструмента
Позиции и аллокация сделаны вкладками одного экрана, а не двумя пунктами навигации:
они отвечают на две половины одного вопроса, и десятый пункт в bottom bar оставил бы
по сорок пикселей на подпись. Scope переключается один раз и сразу для всех трёх
экранов — три экрана с разными scope были бы ловушкой.

Позиция без цены показывается прочерком, никогда нулём: её стоимость не входит в
итоги выше, и 0 ₽ читался бы как «ничего не стоит» вместо «неизвестно». То же для
общей прибыли, когда в портфеле есть хоть одна неоценённая бумага.

Подписи ключей берутся по .value, а не по .name: генератор придумывает Dart-имя
(assetClass для asset_class), и словарь, ключёванный по .name, молча не совпадает
никогда.

Тесты пампят экран на высокой поверхности: вкладки это длинные списки, а ListView
строит только видимое, и на дефолтных 800x600 таблица позиций просто не существует.
2026-09-18 14:21:06 +03:00
Dmitry 28ff63bdfa chore(app): сгенерированный Dart-клиент с методами аналитики
just gen-client после новых роутов.
2026-09-18 14:20:50 +03:00
Dmitry 90ba198c2a chore(app): сгенерированный Dart-клиент из OpenAPI
openapi-generator-cli -g dart-dio. Генерируется, но коммитится: CI ловит дрейф
openapi/openapi.json, а приложение собирается без запуска генератора.
Пересобирается через just gen-client после любого изменения роутов.
2026-09-18 13:44:09 +03:00
Dmitry ce0966fca2 feat(app): Flutter-клиент — логин, дашборд, потоки, категории, транзакции, правила
Riverpod + go_router с auth-guard, NavigationRail на широком экране и bottom bar
на узком. Токены в flutter_secure_storage, на web access живёт в памяти.
Интерцептор подставляет токен и делает ровно один refresh на 401.

Деньги приходят строками и форматируются через Decimal: парсить их в double
значило бы терять копейки ровно там, где бэкенд их бережёт.
2026-09-18 13:44:09 +03:00