Files
fin-tracker/app/lib/features/shell/analytics_page.dart
T
Dmitry 62d36aa3e8 feat(app): новый визуальный язык, верхняя навигация, портфели, создание счетов и ручные события
Тема и общие виджеты (AssetIcon, ServiceMark, HelpTip, глоссарий), верхняя панель TopNav и вкладки Аналитики вместо NavSidebar, иконки PWA. Экраны: портфели, создание брокерского счёта, ручное событие, правка инструмента, Обзор карточками по скоупам и таблица активов, пересчёт метрик с опросом /metrics/status. design-system.md обновлён.
2026-09-19 22:14:21 +03:00

113 lines
3.7 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../portfolio/holdings_tab.dart';
import '../portfolio/overview_tiles.dart';
import '../portfolio/scope_selector.dart';
/// Аналитика → Общее. On a wide surface it is Snowball's overview — the four headline figures
/// for the chosen scope, then the value chart, returns and benchmark comparison; the other
/// analytics screens are one tab away (`AnalyticsTabs`). On a phone there is no tab strip, so
/// the same entry is a list of the sections instead.
///
/// The sections keep their own top-level routes from the contract (`/income`, `/rebalance`,
/// `/goals`, `/tax`) and are deep-linkable; `alsoMatches` in `nav_destinations.dart` keeps
/// Аналитика highlighted inside any of them.
class AnalyticsHubPage extends ConsumerWidget {
const AnalyticsHubPage({super.key});
static const _entries = [
(
path: '/income',
icon: Icons.payments_outlined,
title: 'Доходы',
subtitle: 'Календарь дивидендов и купонов, история выплат, прогноз на 12 месяцев',
),
(
path: '/rebalance',
icon: Icons.balance,
title: 'Ребалансировка',
subtitle:
'Целевые веса портфеля и рекомендации, что докупить или продать',
),
(
path: '/goals',
icon: Icons.flag_outlined,
title: 'Цели',
subtitle: 'Накопительные цели и прогноз их достижения по текущему тренду',
),
(
path: '/tax',
icon: Icons.receipt_long_outlined,
title: 'Налоги',
subtitle: 'Оценка налога за год и лоты с датой ЛДВ — для сверки со справкой брокера',
),
];
@override
Widget build(BuildContext context, WidgetRef ref) {
if (MediaQuery.sizeOf(context).width >= 600) return const _Overview();
return const _SectionList();
}
}
class _Overview extends StatelessWidget {
const _Overview();
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
const Padding(
padding: EdgeInsets.fromLTRB(16, 8, 16, 0),
child: Align(
alignment: Alignment.centerRight,
child: ScopeSelector(),
),
),
const Expanded(
child: HoldingsTab(
sections: HoldingsSections.overview,
header: OverviewTiles(),
),
),
],
),
);
}
}
class _SectionList extends StatelessWidget {
const _SectionList();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Аналитика')),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
for (final e in AnalyticsHubPage._entries)
Card(
child: ListTile(
leading: Icon(e.icon),
title: Text(e.title),
subtitle: Text(e.subtitle),
trailing: const Icon(Icons.chevron_right),
onTap: () => context.go(e.path),
),
),
const SizedBox(height: 12),
Text(
'Сравнение с бенчмарками живёт на экране «Портфель»: обгон индекса — '
'свойство портфеля, а не отдельная тема.',
style: Theme.of(context).textTheme.bodySmall,
),
],
),
);
}
}