feat(insights): first isights impemetation

This commit is contained in:
misha
2026-11-09 15:35:10 +03:00
parent 67920b2016
commit 45d072cff4
63 changed files with 6374 additions and 1465 deletions

View File

@@ -0,0 +1,54 @@
import 'dart:convert';
import 'dart:io';
import 'package:eatvio_app/features/insights/models/insights_payload_model.dart';
import 'package:eatvio_app/features/insights/models/insights_period.dart';
import 'package:eatvio_app/features/insights/repositories/insights_repository.dart';
import 'package:flutter_test/flutter_test.dart';
InsightsPayloadModel loadInsightsPayloadFixture([String name = 'insights_payload.json']) {
final json = jsonDecode(File('test/fixtures/insights/$name').readAsStringSync()) as Map<String, dynamic>;
return InsightsPayloadModel.fromJson(json);
}
/// Подмена [InsightsRepository] для виджет-тестов: без сети, с журналом вызовов.
class FakeInsightsRepository extends Fake implements InsightsRepository {
FakeInsightsRepository({InsightsPayloadModel? payload, this.error, this.heightCm = 176}) : payload = payload ?? loadInsightsPayloadFixture();
InsightsPayloadModel payload;
/// Если задан — выбрасывается из [getInsights] (например, [InsightsForbidden]).
Object? error;
int? heightCm;
final List<({String? slug, BodyPeriod period})> insightsRequests = [];
final List<double> addedWeights = [];
final List<({int chest, int waist, int hip})> addedCircumferences = [];
@override
Future<int?> getHeightCm({String? slug}) async => heightCm;
@override
Future<InsightsPayloadModel> getInsights({String? slug, required BodyPeriod bodyPeriod, int? heightCm}) async {
insightsRequests.add((slug: slug, period: bodyPeriod));
final failure = error;
if (failure != null) {
throw failure;
}
return payload;
}
@override
Future<void> addWeight({required double weight}) async {
addedWeights.add(weight);
}
@override
Future<void> addCircumference({
required int chestCircumference,
required int waistCircumference,
required int hipCircumference,
}) async {
addedCircumferences.add((chest: chestCircumference, waist: waistCircumference, hip: hipCircumference));
}
}

View File

@@ -0,0 +1,58 @@
{
"weight": {
"labels": ["01.11", "05.11", "09.11"],
"values": [74.5, 73.9, 73.2],
"last": 73.2,
"delta": -1.3,
"headline": "73.2",
"unit": "кг",
"deltaText": "Снизили на 1.3 кг за период"
},
"bmi": {
"labels": [],
"values": [],
"last": null,
"delta": null,
"headline": "—",
"category": null,
"deltaText": "Укажите рост, чтобы посчитать ИМТ.",
"hint": ""
},
"measure": {
"labels": ["01.11", "09.11"],
"waist": [82, 80.5],
"chest": [96, 96],
"hips": [101, 100],
"headline": "81",
"deltaText": "Талия меньше на 1.5 см за период"
},
"daily": {
"labels": ["03.11", "04.11", "05.11"],
"kcal": [1850, 2010, 1930],
"proteins": [92, 101, 96],
"fats": [60, 66, 63],
"carbs": [210, 222, 215],
"average": 1930,
"percent": 98,
"headline": "1930",
"deltaText": "98% от нормы"
},
"intra": {
"bars": [
{"x": 8.5, "p": 120, "f": 90, "c": 240},
{"x": 13, "p": 160, "f": 130, "c": 300}
],
"satiety": [{"x": 8.5, "y": 3}, {"x": 13, "y": 4}],
"mood": [{"x": 8.5, "y": 4}],
"wake": 7.5,
"sleep": 23,
"wakeLabel": "07:30",
"sleepLabel": "23:00",
"wakeText": "Подъём в 07:30",
"sleepText": "Отход ко сну в 23:00",
"date": "2026-11-09",
"maxDate": "2026-11-09",
"dateLabel": "Сегодня",
"isToday": true
}
}

View File

@@ -0,0 +1,7 @@
{
"weight": {"labels": [], "values": [], "last": null, "delta": null, "headline": "—", "unit": "кг", "deltaText": "Недостаточно данных"},
"bmi": {"labels": [], "values": [], "headline": "—", "deltaText": "Укажите рост, чтобы посчитать ИМТ.", "hint": "Укажите рост, чтобы посчитать ИМТ."},
"measure": {"labels": [], "waist": [], "chest": [], "hips": [], "headline": "—", "deltaText": "Недостаточно данных"},
"daily": {"labels": [], "kcal": [], "proteins": [], "fats": [], "carbs": [], "average": null, "percent": null, "headline": "—", "deltaText": ""},
"intra": {"bars": [], "satiety": [], "mood": [], "wake": null, "sleep": null, "date": "2026-11-09", "maxDate": "2026-11-09", "dateLabel": "Сегодня", "isToday": true}
}