feat(app): новый визуальный язык, верхняя навигация, портфели, создание счетов и ручные события
Тема и общие виджеты (AssetIcon, ServiceMark, HelpTip, глоссарий), верхняя панель TopNav и вкладки Аналитики вместо NavSidebar, иконки PWA. Экраны: портфели, создание брокерского счёта, ручное событие, правка инструмента, Обзор карточками по скоупам и таблица активов, пересчёт метрик с опросом /metrics/status. design-system.md обновлён.
This commit is contained in:
@@ -10,16 +10,18 @@ import 'data/benchmarks_api.dart';
|
||||
import 'labels.dart';
|
||||
import 'providers.dart';
|
||||
|
||||
final benchmarksApiProvider =
|
||||
Provider<BenchmarksApi>((ref) => BenchmarksApi(ref.watch(apiProvider).dio));
|
||||
final benchmarksApiProvider = Provider<BenchmarksApi>(
|
||||
(ref) => BenchmarksApi(ref.watch(apiProvider).dio),
|
||||
);
|
||||
|
||||
/// Benchmark comparison for the current scope. Part of Портфель, not a screen of its own:
|
||||
/// «на сколько я обогнал индекс» is a property of the portfolio, not a separate subject. See
|
||||
/// `docs/ai/offline-cache.md`.
|
||||
final benchmarkRowsProvider = FutureProvider.autoDispose<Cached<List<BenchmarkRow>>>((ref) async {
|
||||
final scope = ref.watch(scopeProvider);
|
||||
return ref.watch(benchmarksApiProvider).compare(scope: scope);
|
||||
});
|
||||
final benchmarkRowsProvider =
|
||||
FutureProvider.autoDispose<Cached<List<BenchmarkRow>>>((ref) async {
|
||||
final scope = ref.watch(scopeProvider);
|
||||
return ref.watch(benchmarksApiProvider).compare(scope: scope);
|
||||
});
|
||||
|
||||
/// The comparison block on Портфель.
|
||||
///
|
||||
@@ -73,13 +75,16 @@ class _PeriodBlock extends StatelessWidget {
|
||||
if (row.dateFrom != null && row.dateTo != null)
|
||||
Text(
|
||||
'${ruDate(row.dateFrom!)} – ${ruDate(row.dateTo!)}',
|
||||
style: theme.textTheme.bodySmall?.copyWith(color: theme.hintColor),
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.hintColor,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
'портфель ${formatPercent(row.portfolioTwr)}',
|
||||
style: theme.textTheme.titleSmall
|
||||
?.copyWith(color: signColor(context, row.portfolioTwr)),
|
||||
style: theme.textTheme.titleSmall?.copyWith(
|
||||
color: signColor(context, row.portfolioTwr),
|
||||
),
|
||||
),
|
||||
if (row.portfolioDaysSkipped > 0) ...[
|
||||
const SizedBox(width: 6),
|
||||
@@ -89,13 +94,14 @@ class _PeriodBlock extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
for (final b in row.benchmarks) _BenchmarkRowView(result: b),
|
||||
if (row.hasSkippedDays)
|
||||
if (row.benchmarksSkipDays)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 6),
|
||||
child: Text(
|
||||
'Сетка дат не полностью совпадает: часть дней пропущена, '
|
||||
'сравнение не строго like-for-like.',
|
||||
style: theme.textTheme.bodySmall?.copyWith(color: theme.colorScheme.error),
|
||||
'У индекса нет котировок в часть дней окна — сравнение не строго день в день.',
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.hintColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(height: 20),
|
||||
@@ -131,8 +137,9 @@ class _BenchmarkRowView extends StatelessWidget {
|
||||
child: Text(
|
||||
formatPercent(result.excess),
|
||||
textAlign: TextAlign.right,
|
||||
style: theme.textTheme.bodyMedium
|
||||
?.copyWith(color: signColor(context, result.excess)),
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: signColor(context, result.excess),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -148,7 +155,8 @@ class _PriceIndexChip extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
return Tooltip(
|
||||
message: 'Ценовой индекс: не учитывает дивиденды и систематически занижает '
|
||||
message:
|
||||
'Ценовой индекс: не учитывает дивиденды и систематически занижает '
|
||||
'результат держателя. Сравнение с ним — нижняя граница, а не эталон.',
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 1),
|
||||
@@ -156,7 +164,10 @@ class _PriceIndexChip extends StatelessWidget {
|
||||
color: scheme.errorContainer,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Text('ценовой индекс', style: Theme.of(context).textTheme.labelSmall),
|
||||
child: Text(
|
||||
'ценовой индекс',
|
||||
style: Theme.of(context).textTheme.labelSmall,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -171,7 +182,8 @@ class _SkippedChip extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Tooltip(
|
||||
message: 'В расчёте $side пропущено $days дн. — в эти дни не было цены',
|
||||
message:
|
||||
'В расчёте $side не учтено $days дн.: в эти дни у части позиций не было цены',
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
|
||||
Reference in New Issue
Block a user