style(app): остальные экраны под новый визуальный язык и форматирование
Доходы, ребалансировка, налоги, импорт, цели, здоровье данных, правила, транзакции, категории, cashflow, pending: новые виджеты и токены темы, dart format. Тесты подстроены под новые модели.
This commit is contained in:
@@ -52,14 +52,19 @@ class _DataQualityTile extends StatelessWidget {
|
||||
margin: const EdgeInsets.only(top: 4, right: 10),
|
||||
width: 10,
|
||||
height: 10,
|
||||
decoration: BoxDecoration(color: severityColor(row.severity), shape: BoxShape.circle),
|
||||
decoration: BoxDecoration(
|
||||
color: severityColor(row.severity),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('${row.checkName} (${row.count})',
|
||||
style: Theme.of(context).textTheme.titleSmall),
|
||||
Text(
|
||||
'${row.checkName} (${row.count})',
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(row.detail),
|
||||
],
|
||||
|
||||
@@ -16,12 +16,16 @@ import 'data_quality_list.dart';
|
||||
|
||||
final _dateFmt = DateFormat('dd.MM.yyyy HH:mm');
|
||||
|
||||
final syncStatusProvider = FutureProvider.autoDispose<List<SourceStatus>>((ref) async {
|
||||
final syncStatusProvider = FutureProvider.autoDispose<List<SourceStatus>>((
|
||||
ref,
|
||||
) async {
|
||||
final r = await ref.watch(apiProvider).getSyncApi().syncStatus();
|
||||
return r.data ?? const [];
|
||||
});
|
||||
|
||||
final syncRunsProvider = FutureProvider.autoDispose<List<SyncRunOut>>((ref) async {
|
||||
final syncRunsProvider = FutureProvider.autoDispose<List<SyncRunOut>>((
|
||||
ref,
|
||||
) async {
|
||||
final r = await ref.watch(apiProvider).getSyncApi().syncRuns(limit: 20);
|
||||
return r.data ?? const [];
|
||||
});
|
||||
@@ -74,7 +78,8 @@ class _HealthPageState extends ConsumerState<HealthPage> {
|
||||
await ref.read(apiProvider).getSyncApi().syncTrigger(source_: source);
|
||||
} on DioException catch (e) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(problemMessage(e))));
|
||||
ScaffoldMessenger.of(context)
|
||||
.showSnackBar(SnackBar(content: Text(problemMessage(e))));
|
||||
} finally {
|
||||
ref.invalidate(syncStatusProvider);
|
||||
ref.invalidate(syncRunsProvider);
|
||||
@@ -114,7 +119,10 @@ class _HealthPageState extends ConsumerState<HealthPage> {
|
||||
body: TabBarView(
|
||||
children: [
|
||||
_SourcesTab(onRefresh: _refreshAll, onTrigger: _trigger),
|
||||
_QualityTab(dataQuality: dataQuality, onRefresh: () async => ref.invalidate(dataQualityProvider)),
|
||||
_QualityTab(
|
||||
dataQuality: dataQuality,
|
||||
onRefresh: () async => ref.invalidate(dataQualityProvider),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -149,17 +157,26 @@ class _SourcesTab extends ConsumerWidget {
|
||||
message: 'Источники данных ещё не подключены (фаза 1: ZenMoney, ЦБ).',
|
||||
)
|
||||
: Column(
|
||||
children: [for (final s in rows) _SourceCard(source: s, onTrigger: onTrigger)],
|
||||
children: [
|
||||
for (final s in rows)
|
||||
_SourceCard(source: s, onTrigger: onTrigger),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text('Последние запуски', style: Theme.of(context).textTheme.titleMedium),
|
||||
Text(
|
||||
'Последние запуски',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
AsyncValueView(
|
||||
value: runs,
|
||||
onRetry: () => ref.invalidate(syncRunsProvider),
|
||||
data: (rows) => rows.isEmpty
|
||||
? const EmptyState(icon: Icons.history, message: 'Запусков ещё не было.')
|
||||
? const EmptyState(
|
||||
icon: Icons.history,
|
||||
message: 'Запусков ещё не было.',
|
||||
)
|
||||
: Column(children: [for (final r in rows) _RunTile(run: r)]),
|
||||
),
|
||||
],
|
||||
@@ -217,10 +234,14 @@ class _SourceCard extends StatelessWidget {
|
||||
),
|
||||
subtitle: Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: Text([
|
||||
if (source.lastRunAt != null) 'запуск ${_dateFmt.format(source.lastRunAt!.toLocal())}',
|
||||
if (source.cursor != null) 'курсор ${_shortCursor(source.cursor!)}',
|
||||
].join(' · ')),
|
||||
child: Text(
|
||||
[
|
||||
if (source.lastRunAt != null)
|
||||
'запуск ${_dateFmt.format(source.lastRunAt!.toLocal())}',
|
||||
if (source.cursor != null)
|
||||
'курсор ${_shortCursor(source.cursor!)}',
|
||||
].join(' · '),
|
||||
),
|
||||
),
|
||||
trailing: IconButton(
|
||||
tooltip: 'Запустить синхронизацию',
|
||||
@@ -277,7 +298,12 @@ class _RunTile extends StatelessWidget {
|
||||
children: [
|
||||
content,
|
||||
const SizedBox(height: 8),
|
||||
Text(run.error!, style: TextStyle(color: Theme.of(context).colorScheme.error)),
|
||||
Text(
|
||||
run.error!,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -295,7 +321,10 @@ class _RunTile extends StatelessWidget {
|
||||
_runTitle(context),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 2, bottom: 6),
|
||||
child: Text(subtitle, style: Theme.of(context).textTheme.bodySmall),
|
||||
child: Text(
|
||||
subtitle,
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
content,
|
||||
],
|
||||
@@ -317,7 +346,10 @@ class _RunTile extends StatelessWidget {
|
||||
|
||||
Widget _statusChip(BuildContext context, RunStatus? status) {
|
||||
if (status == null) {
|
||||
return const Chip(visualDensity: VisualDensity.compact, label: Text('нет данных'));
|
||||
return const Chip(
|
||||
visualDensity: VisualDensity.compact,
|
||||
label: Text('нет данных'),
|
||||
);
|
||||
}
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
final (label, color) = switch (status) {
|
||||
|
||||
Reference in New Issue
Block a user