Доходы, ребалансировка, налоги, импорт, цели, здоровье данных, правила, транзакции, категории, cashflow, pending: новые виджеты и токены темы, dart format. Тесты подстроены под новые модели.
114 lines
3.3 KiB
Dart
114 lines
3.3 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../../core/cache/cached.dart';
|
|
import '../../core/widgets/stale_banner.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) {
|
|
final stale = oldestFetch([
|
|
ref.watch(taxSummaryProvider).valueOrNull?.fetchedAt,
|
|
ref.watch(taxLotsProvider).valueOrNull?.fetchedAt,
|
|
]);
|
|
|
|
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: Column(
|
|
children: [
|
|
const EstimateBanner(),
|
|
if (stale != null)
|
|
Padding(
|
|
padding: const EdgeInsets.fromLTRB(16, 0, 16, 12),
|
|
child: StaleBanner(fetchedAt: stale),
|
|
),
|
|
const 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?.data;
|
|
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;
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|