feat(app): новый визуальный язык, верхняя навигация, портфели, создание счетов и ручные события
Тема и общие виджеты (AssetIcon, ServiceMark, HelpTip, глоссарий), верхняя панель TopNav и вкладки Аналитики вместо NavSidebar, иконки PWA. Экраны: портфели, создание брокерского счёта, ручное событие, правка инструмента, Обзор карточками по скоупам и таблица активов, пересчёт метрик с опросом /metrics/status. design-system.md обновлён.
This commit is contained in:
@@ -8,41 +8,63 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
NetWorthBreakdown _breakdown() => NetWorthBreakdown(
|
||||
accounts: const [],
|
||||
byCurrency: const {'RUB': '123456.78'},
|
||||
d: DateTime(2026, 9, 17),
|
||||
debtRub: '1000',
|
||||
investmentRub: '2000',
|
||||
liquidRub: '3000',
|
||||
missingFxCount: 0,
|
||||
savingsRub: '4000',
|
||||
totalRub: '123456.78',
|
||||
);
|
||||
accounts: const [],
|
||||
byCurrency: const {'RUB': '123456.78'},
|
||||
d: DateTime(2026, 9, 17),
|
||||
debtRub: '1000',
|
||||
investmentRub: '2000',
|
||||
liquidRub: '3000',
|
||||
missingFxCount: 0,
|
||||
savingsRub: '4000',
|
||||
totalRub: '123456.78',
|
||||
);
|
||||
|
||||
RunwayOut _runway() => RunwayOut(
|
||||
asOf: DateTime(2026, 9, 17),
|
||||
avgBaseline3mRub: '0',
|
||||
liquidReserveRub: '0',
|
||||
runwayMonths: null,
|
||||
);
|
||||
asOf: DateTime(2026, 9, 17),
|
||||
avgBaseline3mRub: '0',
|
||||
liquidReserveRub: '0',
|
||||
runwayMonths: null,
|
||||
);
|
||||
|
||||
// Every provider HomePage watches needs an override — including
|
||||
// portfolioSummaryHomeProvider, easy to forget since it renders nothing on an
|
||||
// empty ledger. Without it, HomePage falls through to the real apiProvider,
|
||||
// which now also opens a real ResponseCacheDatabase (see docs/ai/offline-cache.md).
|
||||
List<Override> _overrides({DateTime? staleAt}) => [
|
||||
netWorthBreakdownProvider.overrideWith((ref) => Cached(_breakdown(), fetchedAt: staleAt)),
|
||||
netWorthSeriesProvider.overrideWith((ref) => Cached(const <NetWorthDay>[], fetchedAt: staleAt)),
|
||||
cashflowThisMonthProvider.overrideWith((ref) => Cached(null, fetchedAt: staleAt)),
|
||||
cashflowLast12Provider.overrideWith((ref) => Cached(const <CashFlowMonth>[], fetchedAt: staleAt)),
|
||||
runwayProvider.overrideWith((ref) => Cached(_runway(), fetchedAt: staleAt)),
|
||||
metricsStatusProvider.overrideWith((ref) => Cached(null, fetchedAt: staleAt)),
|
||||
dataQualityProvider.overrideWith((ref) => Cached(const <DataQualityRow>[], fetchedAt: staleAt)),
|
||||
portfolioSummaryHomeProvider.overrideWith((ref) => Cached(null, fetchedAt: staleAt)),
|
||||
];
|
||||
netWorthBreakdownProvider.overrideWith(
|
||||
(ref) => Cached(_breakdown(), fetchedAt: staleAt),
|
||||
),
|
||||
netWorthSeriesProvider.overrideWith(
|
||||
(ref) => Cached(const <NetWorthDay>[], fetchedAt: staleAt),
|
||||
),
|
||||
cashflowThisMonthProvider.overrideWith(
|
||||
(ref) => Cached(null, fetchedAt: staleAt),
|
||||
),
|
||||
cashflowLast12Provider.overrideWith(
|
||||
(ref) => Cached(const <CashFlowMonth>[], fetchedAt: staleAt),
|
||||
),
|
||||
runwayProvider.overrideWith((ref) => Cached(_runway(), fetchedAt: staleAt)),
|
||||
metricsStatusProvider.overrideWith(
|
||||
(ref) => Cached(
|
||||
MetricsStatusOut(lastRefresh: null, consistent: true, refreshing: false),
|
||||
fetchedAt: staleAt,
|
||||
),
|
||||
),
|
||||
dataQualityProvider.overrideWith(
|
||||
(ref) => Cached(const <DataQualityRow>[], fetchedAt: staleAt),
|
||||
),
|
||||
portfolioSummaryHomeProvider.overrideWith(
|
||||
(ref) => Cached(null, fetchedAt: staleAt),
|
||||
),
|
||||
scopeCardsProvider.overrideWith(
|
||||
(ref) => Cached(const <ScopeCardOut>[], fetchedAt: staleAt),
|
||||
),
|
||||
];
|
||||
|
||||
void main() {
|
||||
testWidgets('Обзор renders stat tiles from a net worth breakdown', (tester) async {
|
||||
testWidgets('Обзор renders stat tiles from a net worth breakdown', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
overrides: _overrides(),
|
||||
@@ -60,15 +82,21 @@ void main() {
|
||||
expect(find.textContaining('Нет соединения'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('shows the offline banner when the dashboard came from the cache', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
overrides: _overrides(staleAt: DateTime(2026, 9, 10, 8, 30)),
|
||||
child: const MaterialApp(home: HomePage()),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
testWidgets(
|
||||
'shows the offline banner when the dashboard came from the cache',
|
||||
(tester) async {
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
overrides: _overrides(staleAt: DateTime(2026, 9, 10, 8, 30)),
|
||||
child: const MaterialApp(home: HomePage()),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.textContaining('Нет соединения — данные на 10.09.2026 08:30'), findsOneWidget);
|
||||
});
|
||||
expect(
|
||||
find.textContaining('Нет соединения — данные на 10.09.2026 08:30'),
|
||||
findsOneWidget,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user