forked from eatvio/eatvio_app
feat(insights): added kbju fetching for analitics
This commit is contained in:
@@ -17,6 +17,9 @@ abstract class DiaryDayEntity with _$DiaryDayEntity {
|
||||
@JsonKey(name: "chosen_plan_id") required int? chosenPlanId,
|
||||
@JsonKey(name: "day_of_chosen_diet") required int? dayOfChosenDiet,
|
||||
@JsonKey(name: "chosen_diet", fromJson: parseDiet) required DiaryDietModel? diet,
|
||||
|
||||
/// Slug владельца дневника; `null`, если сервер его не вернул.
|
||||
String? ownerSlug,
|
||||
}) = _DiaryDayEntity;
|
||||
|
||||
const DiaryDayEntity._();
|
||||
|
||||
@@ -48,6 +48,10 @@ abstract class DiaryDayModel with _$DiaryDayModel {
|
||||
@JsonKey(name: "chosen_plan_id") required int? chosenPlanId,
|
||||
@JsonKey(name: "day_of_chosen_diet") required int? dayOfChosenDiet,
|
||||
@JsonKey(name: "chosen_diet", fromJson: parseDiet) required DiaryDietModel? diet,
|
||||
|
||||
/// Владелец дневника (`user` в `DiaryResource`): по `slug` консультант
|
||||
/// запрашивает `/api/profile/insights/{slug}`.
|
||||
@JsonKey(name: "user") DiaryOwnerModel? owner,
|
||||
}) = _DiaryDayModel;
|
||||
const DiaryDayModel._();
|
||||
factory DiaryDayModel.fromJson(Map<String, dynamic> json) => _$DiaryDayModelFromJson(json);
|
||||
@@ -68,6 +72,7 @@ abstract class DiaryDayModel with _$DiaryDayModel {
|
||||
wakeup: wakeup,
|
||||
retire: retire,
|
||||
diet: diet,
|
||||
ownerSlug: owner?.slug,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -81,6 +86,13 @@ DiaryDietModel? parseDiet(dynamic json) {
|
||||
return DiaryDietModel.fromJson(json['diet']);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class DiaryOwnerModel with _$DiaryOwnerModel {
|
||||
const factory DiaryOwnerModel({int? id, String? slug}) = _DiaryOwnerModel;
|
||||
|
||||
factory DiaryOwnerModel.fromJson(Map<String, dynamic> json) => _$DiaryOwnerModelFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class DiaryDietModel with _$DiaryDietModel {
|
||||
const factory DiaryDietModel({required int id, required int duration, required String name}) = _DiaryDietModel;
|
||||
|
||||
@@ -22,6 +22,7 @@ import 'package:eatvio_app/features/diary/presentation/widgets/meal_widget.dart'
|
||||
import 'package:eatvio_app/features/diary/presentation/widgets/wakeup_and_retire_widget.dart';
|
||||
import 'package:eatvio_app/features/insights/presentation/widgets/insights_card.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
@@ -49,7 +50,12 @@ class UserDiaryScreen extends StatelessWidget {
|
||||
}
|
||||
|
||||
class DiaryScreenContent extends StatefulWidget {
|
||||
const DiaryScreenContent({super.key, this.diaryId, this.date, this.ownerSlug});
|
||||
const DiaryScreenContent({
|
||||
super.key,
|
||||
this.diaryId,
|
||||
this.date,
|
||||
this.ownerSlug,
|
||||
});
|
||||
final int? diaryId;
|
||||
final DateTime? date;
|
||||
final String? ownerSlug;
|
||||
@@ -60,9 +66,14 @@ class DiaryScreenContent extends StatefulWidget {
|
||||
class _DiaryScreenContentState extends State<DiaryScreenContent> {
|
||||
bool _isDiaryFinishing = false;
|
||||
|
||||
/// Slug владельца: из `user.slug` дневника, иначе из параметра экрана.
|
||||
String? _ownerSlug(DiaryStateLoaded state) {
|
||||
return state.diary.ownerSlug ?? widget.ownerSlug;
|
||||
}
|
||||
|
||||
/// Консультант видит аналитику клиента только при известном slug владельца.
|
||||
bool _canShowInsights(DiaryStateLoaded state) {
|
||||
return !state.isConsultantView || widget.ownerSlug != null;
|
||||
return !state.isConsultantView || _ownerSlug(state) != null;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -122,9 +133,9 @@ class _DiaryScreenContentState extends State<DiaryScreenContent> {
|
||||
],
|
||||
if (state is DiaryStateLoaded && _canShowInsights(state))
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 12, 16, 0),
|
||||
child: InsightsCard(
|
||||
slug: state.isConsultantView ? widget.ownerSlug : null,
|
||||
padding: const EdgeInsets.fromLTRB(4, 12, 4, 12),
|
||||
child: _DiaryInsights(
|
||||
slug: state.isConsultantView ? _ownerSlug(state) : null,
|
||||
canAdd: !state.isConsultantView,
|
||||
),
|
||||
),
|
||||
@@ -267,3 +278,45 @@ class _DiaryScreenContentState extends State<DiaryScreenContent> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Блок «Аналитика» в дневнике. Для своего дневника перезапрашивает данные,
|
||||
/// когда в том же дне меняются приёмы пищи или время подъёма/отбоя: от них
|
||||
/// зависят КБЖУ-графики. Чужой дневник консультант не редактирует.
|
||||
class _DiaryInsights extends StatefulWidget {
|
||||
const _DiaryInsights({required this.slug, required this.canAdd});
|
||||
|
||||
final String? slug;
|
||||
final bool canAdd;
|
||||
|
||||
@override
|
||||
State<_DiaryInsights> createState() => _DiaryInsightsState();
|
||||
}
|
||||
|
||||
class _DiaryInsightsState extends State<_DiaryInsights> {
|
||||
int _refreshToken = 0;
|
||||
|
||||
bool _isOwnDiaryMutation(DiaryState previous, DiaryState current) {
|
||||
if (!widget.canAdd ||
|
||||
previous is! DiaryStateLoaded ||
|
||||
current is! DiaryStateLoaded ||
|
||||
previous.diary.id != current.diary.id) {
|
||||
return false;
|
||||
}
|
||||
return !listEquals(previous.diary.meals, current.diary.meals) ||
|
||||
previous.diary.wakeup != current.diary.wakeup ||
|
||||
previous.diary.retire != current.diary.retire;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocListener<DiaryCubit, DiaryState>(
|
||||
listenWhen: _isOwnDiaryMutation,
|
||||
listener: (context, state) => setState(() => _refreshToken++),
|
||||
child: InsightsCard(
|
||||
slug: widget.slug,
|
||||
canAdd: widget.canAdd,
|
||||
refreshToken: _refreshToken,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user