From 96e80ced38eb26db4aaa8e1724a2d4f56319a515 Mon Sep 17 00:00:00 2001 From: Username1548 Date: Tue, 17 Jun 2025 13:33:41 +0700 Subject: [PATCH] feat(consultantRating): added api requests and state to consultant rating page --- lib/core/const.dart | 1 + lib/core/locator_service.dart | 8 +- .../models/rating_model.dart | 36 +++++- .../models/statistics_model.dart | 19 ++++ .../consultant_rating_screen.dart | 103 ++++++++++-------- .../cubit/consultant_rating_cubit.dart | 32 ++++++ .../cubit/consultant_rating_state.dart | 16 +++ .../widgets/period_filter_widget.dart | 72 +++++++----- .../widgets/rating_history_item.dart | 3 +- .../widgets/rating_history_widget.dart | 98 +++++++++-------- .../widgets/statistic_widget.dart | 25 +++-- .../consultant_rating_repository.dart | 26 +++++ .../services/consultant_rating_service.dart | 18 +++ .../menu/presentation/pages/menu_screen.dart | 10 ++ 14 files changed, 335 insertions(+), 132 deletions(-) create mode 100644 lib/features/consultant_rating/models/statistics_model.dart create mode 100644 lib/features/consultant_rating/presentation/cubit/consultant_rating_cubit.dart create mode 100644 lib/features/consultant_rating/presentation/cubit/consultant_rating_state.dart create mode 100644 lib/features/consultant_rating/repositories/consultant_rating_repository.dart create mode 100644 lib/features/consultant_rating/services/consultant_rating_service.dart diff --git a/lib/core/const.dart b/lib/core/const.dart index 618a9866..31c47e9c 100644 --- a/lib/core/const.dart +++ b/lib/core/const.dart @@ -1,4 +1,5 @@ String baseUrl = 'https://eatvio.ru'; +// String baseUrl = 'https://dev2.eatvio.com'; const List durationValues = [7, 14, 21]; diff --git a/lib/core/locator_service.dart b/lib/core/locator_service.dart index accb729d..82eac777 100644 --- a/lib/core/locator_service.dart +++ b/lib/core/locator_service.dart @@ -14,6 +14,9 @@ import 'package:eatvio_app/features/cancel_plan_bottom_sheet/service/cancel_plan import 'package:eatvio_app/features/complaining/presentation/cubit/complaining_cubit.dart'; import 'package:eatvio_app/features/complaining/repositories/complaining_repository.dart'; import 'package:eatvio_app/features/complaining/services/complaining_service.dart'; +import 'package:eatvio_app/features/consultant_rating/presentation/cubit/consultant_rating_cubit.dart'; +import 'package:eatvio_app/features/consultant_rating/repositories/consultant_rating_repository.dart'; +import 'package:eatvio_app/features/consultant_rating/services/consultant_rating_service.dart'; import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_cubit.dart'; import 'package:eatvio_app/features/consultations/repositories/consultations_repository.dart'; import 'package:eatvio_app/features/consultations/services/consultation_service.dart'; @@ -158,6 +161,7 @@ Future init() async { sl.registerFactory(() => ConsultationsArchiveCubit(consultationsRepository: sl())); sl.registerFactory(() => ConsultantFinancesCubit(consultationsRepository: sl())); sl.registerFactory(() => UserPurchasesCubit(consultationsRepository: sl())); + sl.registerFactory(() => ConsultantRatingCubit(sl())); // Repository sl.registerLazySingleton(() => ChatRepository(chatService: sl())); @@ -186,6 +190,7 @@ Future init() async { sl.registerLazySingleton(() => CreatePlanRepository(createPlanService: sl())); sl.registerLazySingleton(() => NotificationsRepository(notificationsService: sl())); sl.registerFactory(() => MyRecipesRepository(myRecipesService: sl())); + sl.registerFactory(() => ConsultantRatingRepository(sl())); // Services sl.registerLazySingleton(() => ChatService(sl())); @@ -214,8 +219,9 @@ Future init() async { sl.registerLazySingleton(() => CancelPlanService(sl())); sl.registerLazySingleton(() => CreatePlanService(sl())); sl.registerLazySingleton(() => NotificationsService(sl())); - sl.registerLazySingleton(() => MyRecipesService(sl())); sl.registerLazySingleton(() => FirebaseApi()); + sl.registerLazySingleton(() => MyRecipesService(sl())); + sl.registerLazySingleton(() => ConsultantRatingService(sl())); // External sl.registerLazySingleton( diff --git a/lib/features/consultant_rating/models/rating_model.dart b/lib/features/consultant_rating/models/rating_model.dart index 164008dd..870d1c82 100644 --- a/lib/features/consultant_rating/models/rating_model.dart +++ b/lib/features/consultant_rating/models/rating_model.dart @@ -1,16 +1,42 @@ +import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'rating_model.freezed.dart'; part 'rating_model.g.dart'; @freezed -abstract class RatingModel with _$RatingModel { +abstract class RatingPaginationModel with _$RatingPaginationModel { + factory RatingPaginationModel({ + required List data, + }) = _RatingPaginationModel; + factory RatingPaginationModel.fromJson(Map json) => _$RatingPaginationModelFromJson(json); +} + +@freezed +abstract class RatingModel with _$RatingModel { factory RatingModel({ - required DateTime date, - required String title, - required int amount, + @JsonKey(name: 'created_at', fromJson: parseDate) required DateTime date, + @JsonKey(name: 'log_data') required RatingEnum type, + @JsonKey(name: 'log_scores') required int amount, }) = _RatingModel; factory RatingModel.fromJson(Map json) => _$RatingModelFromJson(json); -} \ No newline at end of file +} + +enum RatingEnum { + @JsonValue('ADDED_RECIPE') + addedRecipe, + @JsonValue('ADDED_PUBLIC_DIET') + addedPublicDiet, + @JsonValue('ADDED_BLOG_POST') + addedBlogPost, + @JsonValue('INVITED_A_NEW_USER') + invitedUser, + @JsonValue('HAS_MEDICAL_DEGREE') + hasMedicalDegree, + @JsonValue('CONSULTED') + consulted, + @JsonValue('PRODUCT_NUTRITINAL_VALUE_APPROVED') + addedGlossary; +} diff --git a/lib/features/consultant_rating/models/statistics_model.dart b/lib/features/consultant_rating/models/statistics_model.dart new file mode 100644 index 00000000..bbac675b --- /dev/null +++ b/lib/features/consultant_rating/models/statistics_model.dart @@ -0,0 +1,19 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'statistics_model.freezed.dart'; +part 'statistics_model.g.dart'; + +@freezed +abstract class StatisticsModel with _$StatisticsModel { + + factory StatisticsModel({ + @JsonKey(name: 'count_referal') int? countReferal, + @JsonKey(name: 'count_blog_posts') int? blogPostsCount, + @JsonKey(name: 'count_recipes') int? recipesCount, + @JsonKey(name: 'count_diets') int? dietsCount, + @JsonKey(name: 'count_consultants') int? consultationsCount, + @JsonKey(name: 'count_products_nutritional_values') int? qwe, + }) = _StatisticsModel; + + factory StatisticsModel.fromJson(Map json) => _$StatisticsModelFromJson(json); +} \ No newline at end of file diff --git a/lib/features/consultant_rating/presentation/consultant_rating_screen.dart b/lib/features/consultant_rating/presentation/consultant_rating_screen.dart index 285e5d63..8c6cb3d4 100644 --- a/lib/features/consultant_rating/presentation/consultant_rating_screen.dart +++ b/lib/features/consultant_rating/presentation/consultant_rating_screen.dart @@ -1,12 +1,16 @@ +import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/widgets/custom_outlined_button.dart'; -import 'package:eatvio_app/features/consultant_rating/models/rating_model.dart'; +import 'package:eatvio_app/core/widgets/data_error_widget.dart'; +import 'package:eatvio_app/features/consultant_rating/presentation/cubit/consultant_rating_cubit.dart'; +import 'package:eatvio_app/features/consultant_rating/presentation/cubit/consultant_rating_state.dart'; import 'package:eatvio_app/features/consultant_rating/presentation/widgets/rating_history_widget.dart'; import 'package:eatvio_app/features/consultant_rating/presentation/widgets/rating_widget.dart'; import 'package:eatvio_app/features/consultant_rating/presentation/widgets/statistic_widget.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; class ConsultantRatingScreen extends StatelessWidget { static const name = 'consultantRatingScreen'; @@ -15,7 +19,10 @@ class ConsultantRatingScreen extends StatelessWidget { @override Widget build(BuildContext context) { - return const ConsultantRatingPageContent(); + return BlocProvider( + create: (context) => sl()..getConsultantStatistics()..getScoreLogs(), + child: const ConsultantRatingPageContent(), + ); } } @@ -25,52 +32,56 @@ class ConsultantRatingPageContent extends StatelessWidget { @override Widget build(BuildContext context) { final s = S.of(context); - return Scaffold( - backgroundColor: appTheme.gray100, - appBar: AppBar( - centerTitle: true, - title: Text( - s.rating_and_statistics, - style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), - ), - ), - body: SingleChildScrollView( - child: Column( - children: [ - const SizedBox(height: 16), - const RatingWidget( - rating: 1058, + return BlocBuilder( + builder: (context, state) { + return Scaffold( + backgroundColor: appTheme.gray100, + appBar: AppBar( + centerTitle: true, + title: Text( + s.rating_and_statistics, + style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), - const SizedBox(height: 16), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: CustomOutlinedButton( - text: s.copy_referral_link, - height: 52, - buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), - buttonStyle: ButtonStyle( - side: const WidgetStatePropertyAll(BorderSide.none), - backgroundColor: WidgetStatePropertyAll(appTheme.blueA400), + ), + body: state.error != null + ? DataErrorWidget(onReload: () async { + await context.read().getConsultantStatistics(); + if (!context.mounted) return; + await context.read().getScoreLogs(); + }) + : SingleChildScrollView( + child: Column( + children: [ + const SizedBox(height: 16), + const RatingWidget( + rating: 1058, + ), + const SizedBox(height: 16), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: CustomOutlinedButton( + text: s.copy_referral_link, + height: 52, + buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), + buttonStyle: ButtonStyle( + side: const WidgetStatePropertyAll(BorderSide.none), + backgroundColor: WidgetStatePropertyAll(appTheme.blueA400), + ), + onPressed: () {}, + ), + ), + const SizedBox(height: 16), + StatisticWidget(isLoading: state.isStatisticsLoading, statistics: state.statistics,), + const SizedBox(height: 16), + RatingHistoryWidget( + isLoading: state.isRatingLoading, + ratingHistory: state.ratings, + ), + ], + ), ), - onPressed: () {}, - ), - ), - const SizedBox(height: 16), - const StatisticWidget(), - const SizedBox(height: 16), - RatingHistoryWidget( - ratingHistory: [ - RatingModel(date: DateTime.now(), title: ' djkasjdasjkdkjasjkdjkaskjdjklasjkld', amount: 201300), - RatingModel(date: DateTime.now(), title: 'djkasjdasjkdkjasjkdjkaskjdjklasjkld', amount: 89323), - RatingModel(date: DateTime.now(), title: 'djkasjdasjkdkjasjkdjkaskjdjklasjkld', amount: 20), - RatingModel(date: DateTime.now(), title: 'djkasjdasjkdkjasjkdjkaskjdjklasjkld', amount: 1212), - RatingModel(date: DateTime.now(), title: 'qweqweeq', amount: 323), - RatingModel(date: DateTime.now(), title: 'ee', amount: 1), - ], - ), - ], - ), - ), + ); + }, ); } } diff --git a/lib/features/consultant_rating/presentation/cubit/consultant_rating_cubit.dart b/lib/features/consultant_rating/presentation/cubit/consultant_rating_cubit.dart new file mode 100644 index 00000000..291f450c --- /dev/null +++ b/lib/features/consultant_rating/presentation/cubit/consultant_rating_cubit.dart @@ -0,0 +1,32 @@ +import 'dart:developer'; + +import 'package:eatvio_app/features/consultant_rating/presentation/cubit/consultant_rating_state.dart'; +import 'package:eatvio_app/features/consultant_rating/repositories/consultant_rating_repository.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class ConsultantRatingCubit extends Cubit { + final ConsultantRatingRepository repository; + ConsultantRatingCubit(this.repository) : super(ConsultantRatingState()); + + Future getConsultantStatistics({String? date}) async { + try { + emit(state.copyWith(isStatisticsLoading: true)); + final statistics = await repository.getConsultantStatistics(date: date); + emit(state.copyWith(isStatisticsLoading: false, statistics: statistics, error: null)); + } catch (e, st) { + emit(state.copyWith(isStatisticsLoading: true, error: e.toString())); + log(e.toString(), stackTrace: st); + } + } + + Future getScoreLogs({String? date}) async { + try { + emit(state.copyWith(isRatingLoading: true)); + final scoreLogs = await repository.getScoreLogs(date: date); + emit(state.copyWith(isRatingLoading: false, ratings: scoreLogs, error: null)); + } catch (e, st) { + emit(state.copyWith(isRatingLoading: true, error: e.toString())); + log(e.toString(), stackTrace: st); + } + } +} diff --git a/lib/features/consultant_rating/presentation/cubit/consultant_rating_state.dart b/lib/features/consultant_rating/presentation/cubit/consultant_rating_state.dart new file mode 100644 index 00000000..20cc458f --- /dev/null +++ b/lib/features/consultant_rating/presentation/cubit/consultant_rating_state.dart @@ -0,0 +1,16 @@ +import 'package:eatvio_app/features/consultant_rating/models/rating_model.dart'; +import 'package:eatvio_app/features/consultant_rating/models/statistics_model.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'consultant_rating_state.freezed.dart'; + +@freezed +abstract class ConsultantRatingState with _$ConsultantRatingState { + factory ConsultantRatingState({ + @Default(false) bool isStatisticsLoading, + @Default(false) bool isRatingLoading, + @Default(null) StatisticsModel? statistics, + @Default(null) String? error, + @Default([]) List ratings, + }) = _ConsultantRatingState; +} diff --git a/lib/features/consultant_rating/presentation/widgets/period_filter_widget.dart b/lib/features/consultant_rating/presentation/widgets/period_filter_widget.dart index 7ad0fa7e..3f15be8e 100644 --- a/lib/features/consultant_rating/presentation/widgets/period_filter_widget.dart +++ b/lib/features/consultant_rating/presentation/widgets/period_filter_widget.dart @@ -1,42 +1,29 @@ import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/core/utils/date_time_utils.dart'; import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart'; +import 'package:eatvio_app/features/consultant_rating/presentation/cubit/consultant_rating_cubit.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; class PeriodFilterWidget extends StatefulWidget { - const PeriodFilterWidget({super.key}); - + const PeriodFilterWidget({super.key, required this.isStatistic}); + final bool isStatistic; @override State createState() => _PeriodFilterWidgetState(); } class _PeriodFilterWidgetState extends State { - final List periodDropDownTitles = []; - int periodDropDownIndex = 0; - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - final s = S.of(context); - if (periodDropDownTitles.isEmpty) { - periodDropDownTitles.addAll([ - s.for_whole_period_of_time, - s.for_a_year, - s.for_a_month, - s.for_a_week, - ]); - } - } - + PeriodFilterStatus _currentState = PeriodFilterStatus.all; @override Widget build(BuildContext context) { final s = S.of(context); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - CustomDropDownButton( + CustomDropDownButton( hint: Text( s.choose_diet_duration, style: TextStyles.textSMedium.copyWith(color: appTheme.black), @@ -44,24 +31,59 @@ class _PeriodFilterWidgetState extends State { onChanged: (value) { if (value != null) { setState(() { - periodDropDownIndex = periodDropDownTitles.indexOf(value); + _currentState = value; }); + if (widget.isStatistic) { + if (value == PeriodFilterStatus.all) { + context.read().getConsultantStatistics(); + return; + } + context.read().getConsultantStatistics(date: value.value); + return; + } + if (value == PeriodFilterStatus.all) { + context.read().getScoreLogs(); + return; + } + context.read().getScoreLogs(date: value.value); + return; } }, - items: periodDropDownTitles + items: PeriodFilterStatus.values .mapIndexed( (index, value) => DropdownMenuItem( - value: periodDropDownTitles[index], + value: PeriodFilterStatus.values[index], child: Text( - periodDropDownTitles[index], + PeriodFilterStatus.values[index].name, style: TextStyles.textMMedium.copyWith(color: appTheme.black), ), ), ) .toList(), - value: periodDropDownTitles[periodDropDownIndex], + value: _currentState, ), ], ); } } + +enum PeriodFilterStatus { + all, + month, + week, + year; + + String get name => switch (this) { + PeriodFilterStatus.all => 'Все периоды', + PeriodFilterStatus.month => 'За месяц', + PeriodFilterStatus.week => 'За неделю', + PeriodFilterStatus.year => 'За год', + }; + + String? get value => switch (this) { + PeriodFilterStatus.all => null, + PeriodFilterStatus.month => DateTime.now().subtract(const Duration(days: 30)).format('yyyy-MM-dd'), + PeriodFilterStatus.week => DateTime.now().subtract(const Duration(days: 7)).format('yyyy-MM-dd'), + PeriodFilterStatus.year => DateTime.now().subtract(const Duration(days: 365)).format('yyyy-MM-dd'), + }; +} diff --git a/lib/features/consultant_rating/presentation/widgets/rating_history_item.dart b/lib/features/consultant_rating/presentation/widgets/rating_history_item.dart index 3a3cff6e..4bafd1b3 100644 --- a/lib/features/consultant_rating/presentation/widgets/rating_history_item.dart +++ b/lib/features/consultant_rating/presentation/widgets/rating_history_item.dart @@ -11,7 +11,6 @@ class RatingHistoryItem extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - height: 80, padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: appTheme.gray100, @@ -29,7 +28,7 @@ class RatingHistoryItem extends StatelessWidget { date.format('dd.MM.yyyy'), style: TextStyles.textXSSemibold.copyWith(color: appTheme.gray), ), - const Spacer(), + const SizedBox(height: 12), Text( title, style: TextStyles.textMMedium.copyWith(color: appTheme.black), diff --git a/lib/features/consultant_rating/presentation/widgets/rating_history_widget.dart b/lib/features/consultant_rating/presentation/widgets/rating_history_widget.dart index 0413dda5..d6f8969b 100644 --- a/lib/features/consultant_rating/presentation/widgets/rating_history_widget.dart +++ b/lib/features/consultant_rating/presentation/widgets/rating_history_widget.dart @@ -1,5 +1,6 @@ import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/core/widgets/main_background_bouble.dart'; import 'package:eatvio_app/features/consultant_rating/models/rating_model.dart'; import 'package:eatvio_app/features/consultant_rating/presentation/widgets/period_filter_widget.dart'; @@ -9,7 +10,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; class RatingHistoryWidget extends StatelessWidget { - const RatingHistoryWidget({super.key, required this.ratingHistory}); + const RatingHistoryWidget({super.key, required this.ratingHistory, required this.isLoading}); + final bool isLoading; final List ratingHistory; @override Widget build(BuildContext context) { @@ -20,50 +22,60 @@ class RatingHistoryWidget extends StatelessWidget { right: 16, top: 16, ), - child: Column( - children: [ - Row( - children: [ - Text( - s.rating_history, - style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), - ), - const Spacer(), - SvgPicture.asset( - 'assets/icons/question_mark.svg', - height: 22, - width: 22, - colorFilter: ColorFilter.mode( - appTheme.blueA400, - BlendMode.srcIn, + child: isLoading + ? const Center(child: Loader()) + : Column( + children: [ + Row( + children: [ + Text( + s.rating_history, + style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), + ), + const Spacer(), + SvgPicture.asset( + 'assets/icons/question_mark.svg', + height: 22, + width: 22, + colorFilter: ColorFilter.mode( + appTheme.blueA400, + BlendMode.srcIn, + ), + ), + ], ), - ), - ], - ), - const SizedBox( - height: 16, - ), - const PeriodFilterWidget(), - const SizedBox( - height: 12, - ), - ListView.builder( - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, index) { - return Padding( - padding: index == ratingHistory.length - 1 ? EdgeInsets.zero : const EdgeInsets.only(bottom: 12), - child: RatingHistoryItem( - title: ratingHistory[index].title, - amount: ratingHistory[index].amount, - date: ratingHistory[index].date, + const SizedBox( + height: 16, ), - ); - }, - itemCount: ratingHistory.length, - shrinkWrap: true, - ), - ], - ), + const PeriodFilterWidget(isStatistic: false), + const SizedBox( + height: 12, + ), + ListView.builder( + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + return Padding( + padding: index == ratingHistory.length - 1 ? EdgeInsets.zero : const EdgeInsets.only(bottom: 12), + child: RatingHistoryItem( + title: switch (ratingHistory[index].type) { + RatingEnum.addedRecipe => 'Добавление рецепта', + RatingEnum.addedPublicDiet => 'Добавление публичной диеты', + RatingEnum.addedBlogPost => 'Добавление поста', + RatingEnum.invitedUser => 'Регистрация по реф. ссылке', + RatingEnum.hasMedicalDegree => 'Получение мед. степени', + RatingEnum.consulted => 'Консультация', + RatingEnum.addedGlossary => 'Добавление продукта в глоссарий', + }, + amount: ratingHistory[index].amount, + date: ratingHistory[index].date, + ), + ); + }, + itemCount: ratingHistory.length, + shrinkWrap: true, + ), + ], + ), ); } } diff --git a/lib/features/consultant_rating/presentation/widgets/statistic_widget.dart b/lib/features/consultant_rating/presentation/widgets/statistic_widget.dart index b7f97e2b..3be44129 100644 --- a/lib/features/consultant_rating/presentation/widgets/statistic_widget.dart +++ b/lib/features/consultant_rating/presentation/widgets/statistic_widget.dart @@ -1,6 +1,8 @@ import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/core/widgets/main_background_bouble.dart'; +import 'package:eatvio_app/features/consultant_rating/models/statistics_model.dart'; import 'package:eatvio_app/features/consultant_rating/presentation/widgets/period_filter_widget.dart'; import 'package:eatvio_app/features/consultant_rating/presentation/widgets/statistic_bouble.dart'; import 'package:eatvio_app/generated/l10n.dart'; @@ -8,14 +10,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; class StatisticWidget extends StatelessWidget { - const StatisticWidget({super.key}); - + const StatisticWidget({super.key, required this.isLoading, required this.statistics}); + final bool isLoading; + final StatisticsModel? statistics; @override Widget build(BuildContext context) { final s = S.of(context); return MainBackgroundBouble( padding: const EdgeInsets.all(16), - child: Column( + child: isLoading ? const Center(child: Loader()) : Column( children: [ Row( children: [ @@ -38,15 +41,17 @@ class StatisticWidget extends StatelessWidget { const SizedBox( height: 16, ), - const PeriodFilterWidget(), + const PeriodFilterWidget( + isStatistic: true, + ), const SizedBox( height: 12, ), Row( children: [ - Expanded(child: StatisticBouble(number: 12, title: s.invited_people_count)), + Expanded(child: StatisticBouble(number: statistics?.countReferal ?? 0, title: s.invited_people_count)), const SizedBox(width: 8), - Expanded(child: StatisticBouble(number: 12, title: s.published_posts_count)), + Expanded(child: StatisticBouble(number: statistics?.blogPostsCount ?? 0, title: s.published_posts_count)), ], ), const SizedBox( @@ -54,9 +59,9 @@ class StatisticWidget extends StatelessWidget { ), Row( children: [ - Expanded(child: StatisticBouble(number: 12, title: s.published_recipes_count)), + Expanded(child: StatisticBouble(number: statistics?.recipesCount ?? 0, title: s.published_recipes_count)), const SizedBox(width: 8), - Expanded(child: StatisticBouble(number: 12, title: s.published_diets_count)), + Expanded(child: StatisticBouble(number: statistics?.dietsCount ?? 0, title: s.published_diets_count)), ], ), const SizedBox( @@ -64,9 +69,9 @@ class StatisticWidget extends StatelessWidget { ), Row( children: [ - Expanded(child: StatisticBouble(number: 12, title: s.consultations_count)), + Expanded(child: StatisticBouble(number: statistics?.consultationsCount ?? 0, title: s.consultations_count)), const SizedBox(width: 8), - Expanded(child: StatisticBouble(number: 12, title: s.active_consultations_count)), + Expanded(child: StatisticBouble(number: statistics?.qwe ?? 0, title: s.active_consultations_count)), ], ), const SizedBox( diff --git a/lib/features/consultant_rating/repositories/consultant_rating_repository.dart b/lib/features/consultant_rating/repositories/consultant_rating_repository.dart new file mode 100644 index 00000000..b4ac3770 --- /dev/null +++ b/lib/features/consultant_rating/repositories/consultant_rating_repository.dart @@ -0,0 +1,26 @@ +import 'package:eatvio_app/features/consultant_rating/models/rating_model.dart'; +import 'package:eatvio_app/features/consultant_rating/models/statistics_model.dart'; +import 'package:eatvio_app/features/consultant_rating/services/consultant_rating_service.dart'; + +class ConsultantRatingRepository { + final ConsultantRatingService service; + + ConsultantRatingRepository(this.service); + + Future getConsultantStatistics({String? date}) async { + try { + return await service.getConsultantStatistics(date: date); + } catch (e) { + rethrow; + } + } + + Future> getScoreLogs({String? date}) async { + try { + final data = (await service.getScoreLogs(date: date)).data; + return data; + } catch (e) { + rethrow; + } + } +} diff --git a/lib/features/consultant_rating/services/consultant_rating_service.dart b/lib/features/consultant_rating/services/consultant_rating_service.dart new file mode 100644 index 00000000..c4412afb --- /dev/null +++ b/lib/features/consultant_rating/services/consultant_rating_service.dart @@ -0,0 +1,18 @@ +import 'package:dio/dio.dart'; +import 'package:eatvio_app/features/consultant_rating/models/rating_model.dart'; +import 'package:eatvio_app/features/consultant_rating/models/statistics_model.dart'; + +import 'package:retrofit/retrofit.dart'; + +part 'consultant_rating_service.g.dart'; + +@RestApi() +abstract class ConsultantRatingService { + factory ConsultantRatingService(Dio dio, {String baseUrl}) = _ConsultantRatingService; + + @GET('/api/consultants/rating/statistics') + Future getConsultantStatistics({@Query('filters[duration][\$gte]') String? date}); + + @GET('/api/consultants/score/logs') + Future getScoreLogs({@Query('filters[duration][\$gte]') String? date}); +} diff --git a/lib/features/menu/presentation/pages/menu_screen.dart b/lib/features/menu/presentation/pages/menu_screen.dart index f657c6a7..7409a3a5 100644 --- a/lib/features/menu/presentation/pages/menu_screen.dart +++ b/lib/features/menu/presentation/pages/menu_screen.dart @@ -4,6 +4,7 @@ import 'package:eatvio_app/core/token_storage.dart'; import 'package:eatvio_app/core/utils/image_constant.dart'; import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/features/consultant_rating/presentation/consultant_rating_screen.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/consultant_finances_screen.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/user_purchases_screen.dart'; import 'package:eatvio_app/features/my_diets/presentation/my_diets_screen.dart'; @@ -49,6 +50,15 @@ class MenuScreen extends StatelessWidget { GoRouter.of(context).push(UserPurchasesScreen.path); }, ), + if (profileModel.roles.contains('consultant')) _divider(context), + if (profileModel.roles.contains('consultant')) + _tile( + leading: 'assets/icons/finance.svg', + title: 'Мой рейтинг', + onTap: () { + GoRouter.of(context).push(ConsultantRatingScreen.path); + }, + ), _divider(context), _tile(leading: ImageConstant.icnGlossary, title: 'Глоссарий', onTap: () => GoRouter.of(context).push('/glossary-screen')), _divider(context),