feat(app): экраны импорта отчётов, целей, доходов, ребалансировки, налогов и бенчмарков
Импорт (/imports, /instruments/pending) и фаза 4 (/goals, /income, /rebalance, /tax, аналитика-хаб с benchmarks_card) — по docs/ai/import-contract.md и docs/ai/phase4-contract.md. file_picker для загрузки отчёта.
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'data/tax_api.dart';
|
||||
import 'lots_tab.dart';
|
||||
import 'providers.dart';
|
||||
import 'summary_tab.dart';
|
||||
|
||||
/// Налоги: the year summary and the open-lot list with ЛДВ dates.
|
||||
///
|
||||
/// The word «оценка» is on the screen itself, not in a tooltip: the tax agent is the
|
||||
/// broker, and these numbers exist so that the broker's statement can be checked against
|
||||
/// something — not to replace it.
|
||||
class TaxPage extends ConsumerWidget {
|
||||
const TaxPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return DefaultTabController(
|
||||
length: 2,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Налоги (оценка)'),
|
||||
actions: [
|
||||
const _YearSelector(),
|
||||
IconButton(
|
||||
tooltip: 'Обновить',
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: () => invalidateTaxProviders(ref),
|
||||
),
|
||||
],
|
||||
bottom: const TabBar(
|
||||
tabs: [Tab(text: 'Сводка за год'), Tab(text: 'Лоты и ЛДВ')],
|
||||
),
|
||||
),
|
||||
body: const Column(
|
||||
children: [
|
||||
EstimateBanner(),
|
||||
Expanded(child: TabBarView(children: [TaxSummaryTab(), TaxLotsTab()])),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The disclaimer, always visible above both tabs.
|
||||
class EstimateBanner extends ConsumerWidget {
|
||||
const EstimateBanner({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final summary = ref.watch(taxSummaryProvider).valueOrNull;
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
final text = summary?.disclaimer ?? TaxSummary.defaultDisclaimer;
|
||||
|
||||
return Material(
|
||||
color: scheme.tertiaryContainer,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.info_outline, size: 18),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
text,
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _YearSelector extends ConsumerWidget {
|
||||
const _YearSelector();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final year = ref.watch(taxYearProvider);
|
||||
final now = DateTime.now().year;
|
||||
return DropdownButtonHideUnderline(
|
||||
child: DropdownButton<int>(
|
||||
value: year,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
items: [
|
||||
for (var y = now; y >= now - 6; y--)
|
||||
DropdownMenuItem(value: y, child: Text('$y')),
|
||||
],
|
||||
onChanged: (v) {
|
||||
if (v != null) ref.read(taxYearProvider.notifier).state = v;
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user