diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index 0f0ddf45..dfc2101c 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -17,6 +17,7 @@ import 'package:eatvio_app/features/consultant/become_consultant/presentation/pa import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/stepper_become_consultant_screen.dart'; import 'package:eatvio_app/features/consultations/presentation/pages/consultations_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/order_info/presentation/info_by_order_screen.dart'; import 'package:eatvio_app/features/create_diet/presentation/create_diet_screen.dart'; import 'package:eatvio_app/features/create_diet/presentation/diet_preview_screen.dart'; @@ -191,6 +192,11 @@ class AppRouter { name: ConsultantFinancesScreen.name, builder: (context, state) => const ConsultantFinancesScreen(), ), + GoRoute( + path: UserPurchasesScreen.path, + name: UserPurchasesScreen.name, + builder: (context, state) => const UserPurchasesScreen(), + ), GoRoute( path: '/user-finance-screen', name: 'userFinanceScreen', diff --git a/lib/core/locator_service.dart b/lib/core/locator_service.dart index 47a14057..bd2dd469 100644 --- a/lib/core/locator_service.dart +++ b/lib/core/locator_service.dart @@ -31,6 +31,7 @@ import 'package:eatvio_app/features/blog_post_create/repositories/blog_post_repo import 'package:eatvio_app/features/blog_post_create/services/blog_post_service.dart'; import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/new_messages_cubit/new_messages_cubit.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_cubit.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_cubit.dart'; import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart'; import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart'; import 'package:eatvio_app/features/calendar/presentation/bloc/calendar_cubit.dart'; @@ -149,6 +150,7 @@ Future init() async { sl.registerFactory(() => MyRecipesCubit(myRecipesRepository: sl(), aboutRecipeRepository: sl())); sl.registerFactory(() => ConsultationsArchiveCubit(consultationsRepository: sl())); sl.registerFactory(() => ConsultantFinancesCubit(consultationsRepository: sl())); + sl.registerFactory(() => UserPurchasesCubit(consultationsRepository: sl())); // Repository sl.registerLazySingleton(() => ChatRepository(chatService: sl())); diff --git a/lib/features/consultations/presentation/widgets/consultation_card_widget.dart b/lib/features/consultations/presentation/widgets/consultation_card_widget.dart index c9b9f8b2..11322228 100644 --- a/lib/features/consultations/presentation/widgets/consultation_card_widget.dart +++ b/lib/features/consultations/presentation/widgets/consultation_card_widget.dart @@ -185,6 +185,8 @@ class _ConsultationCardWidgetState extends State { children: List.generate(widget.consultationsModel.duration, (index) { final day = givenDaysWithDayNumber.firstWhereOrNull((e) => e.$1 == index); final hasActivity = day != null && day.$2.state != DiaryStateEnum.init; + final canConsultantOpenThisDay = day != null && + (day.$2.state != DiaryStateEnum.init && day.$2.state != DiaryStateEnum.started && day.$2.state != DiaryStateEnum.lastDayStarted); return Padding( padding: const EdgeInsets.only(right: 12), child: Column( @@ -192,7 +194,7 @@ class _ConsultationCardWidgetState extends State { children: [ InkWell( onTap: () { - if (!hasActivity) { + if (!hasActivity || !canConsultantOpenThisDay) { showErrorDialog(context, title: s.you_can_not_open_this_day, description: '${widget.consultationsModel.userName} ${s.has_not_filled_it_yet}'); return; diff --git a/lib/features/consultations/repositories/consultations_repository.dart b/lib/features/consultations/repositories/consultations_repository.dart index 385c851b..15490dc9 100644 --- a/lib/features/consultations/repositories/consultations_repository.dart +++ b/lib/features/consultations/repositories/consultations_repository.dart @@ -3,6 +3,7 @@ import 'package:eatvio_app/features/consultations/models/consultation_model.dart import 'package:eatvio_app/features/consultations/services/consultation_service.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/models/consultant_finance_model.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/models/statistic_model.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/models/user_purchases_model.dart'; class ConsultationsRepository { final ConsultationService consultationService; @@ -45,10 +46,18 @@ class ConsultationsRepository { } Future> getConsultantFinances(String? date, FiltersComparisonEnum? dateComparison) async { - try { + try { return await consultationService.getConsultantFinances(date, dateComparison); } catch (e) { rethrow; } } + + Future> getUserPurchases(String? date, FiltersComparisonEnum? dateComparison) async { + try { + return await consultationService.getUserPurchases(date, dateComparison); + } catch (e) { + rethrow; + } + } } diff --git a/lib/features/consultations/services/consultation_service.dart b/lib/features/consultations/services/consultation_service.dart index 01a64b11..f0a89220 100644 --- a/lib/features/consultations/services/consultation_service.dart +++ b/lib/features/consultations/services/consultation_service.dart @@ -3,6 +3,7 @@ import 'package:eatvio_app/core/enums/filters_comparison_enum.dart'; import 'package:eatvio_app/features/consultations/models/consultation_model.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/models/consultant_finance_model.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/models/statistic_model.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/models/user_purchases_model.dart'; class ConsultationService { final Dio dio; @@ -86,4 +87,23 @@ class ConsultationService { rethrow; } } + + Future> getUserPurchases( + String? date, + FiltersComparisonEnum? dateComparison, + ) async { + try { + final response = await dio.get( + '/api/finances/my_purchases', + queryParameters: { + if (dateComparison != null && date != null) 'filters[start_date]': date, + }, + ); + final data = response.data as Map; + final purchases = data['data'] as List; + return purchases.map((purchase) => UserPurchasesModel.fromJson(purchase as Map)).toList(); + } catch (e) { + rethrow; + } + } } diff --git a/lib/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_cubit.dart b/lib/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_cubit.dart new file mode 100644 index 00000000..55fdbc68 --- /dev/null +++ b/lib/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_cubit.dart @@ -0,0 +1,36 @@ +import 'dart:developer'; + +import 'package:eatvio_app/core/enums/filters_comparison_enum.dart'; +import 'package:eatvio_app/features/consultations/repositories/consultations_repository.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_state.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class UserPurchasesCubit extends Cubit { + final ConsultationsRepository consultationsRepository; + UserPurchasesCubit({ + required this.consultationsRepository, + }) : super( + UserPurchasesState(), + ); + + Future getUserPurchases() async { + try { + emit(state.copyWith(isLoading: true)); + final purchasesModels = await consultationsRepository.getUserPurchases( + state.periodFilterStatus?.value, + state.periodFilterStatus == PeriodFilterStatus.all ? null : FiltersComparisonEnum.greaterThanOrEqual, + ); + + emit(state.copyWith(purchasesModels: purchasesModels, isLoading: false)); + } catch (e) { + emit(state.copyWith(purchasesModels: [], isLoading: false)); + log(e.toString()); + } + } + + Future setPurchasesPeriod(PeriodFilterStatus period) async { + emit(state.copyWith(periodFilterStatus: period)); + await getUserPurchases(); + } +} diff --git a/lib/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_state.dart b/lib/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_state.dart new file mode 100644 index 00000000..7ae22c03 --- /dev/null +++ b/lib/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_state.dart @@ -0,0 +1,15 @@ +import 'package:eatvio_app/features/finances_and_consultations_archive/models/user_purchases_model.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'user_purchases_state.freezed.dart'; + +@freezed +abstract class UserPurchasesState with _$UserPurchasesState { + factory UserPurchasesState({ + @Default([]) List purchasesModels, + @Default(false) isLoading, + @Default(null) String? error, + @Default(null) PeriodFilterStatus? periodFilterStatus, + }) = _UserPurchasesState; +} diff --git a/lib/features/finances_and_consultations_archive/models/user_purchases_model.dart b/lib/features/finances_and_consultations_archive/models/user_purchases_model.dart new file mode 100644 index 00000000..deaf42dc --- /dev/null +++ b/lib/features/finances_and_consultations_archive/models/user_purchases_model.dart @@ -0,0 +1,19 @@ +import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'user_purchases_model.freezed.dart'; +part 'user_purchases_model.g.dart'; + +@freezed +abstract class UserPurchasesModel with _$UserPurchasesModel { + factory UserPurchasesModel({ + @JsonKey(name: 'start_date', fromJson: parseDate) required DateTime startDate, + @JsonKey(name: 'bank_id') required String? bankId, + @JsonKey(name: 'title') required String title, + @JsonKey(name: 'consultantName') required String consultantName, + @JsonKey(name: 'duration') required int duration, + @JsonKey(name: 'price') required int paymentPrice, + }) = _UserPurchasesModel; + + factory UserPurchasesModel.fromJson(Map json) => _$UserPurchasesModelFromJson(json); +} \ No newline at end of file diff --git a/lib/features/finances_and_consultations_archive/presentation/consultant_finances_screen.dart b/lib/features/finances_and_consultations_archive/presentation/consultant_finances_screen.dart index 6c1e93f6..9d1769c4 100644 --- a/lib/features/finances_and_consultations_archive/presentation/consultant_finances_screen.dart +++ b/lib/features/finances_and_consultations_archive/presentation/consultant_finances_screen.dart @@ -38,7 +38,10 @@ class _ConsultantFinancesScreenState extends State { bottom: Radius.circular(12), ), ), - title: const Text('Архив консультаций'), + title: Text( + 'Финансы', + style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), + ), centerTitle: true, ), body: state.isLoading diff --git a/lib/features/finances_and_consultations_archive/presentation/user_purchases_screen.dart b/lib/features/finances_and_consultations_archive/presentation/user_purchases_screen.dart new file mode 100644 index 00000000..e1f9f036 --- /dev/null +++ b/lib/features/finances_and_consultations_archive/presentation/user_purchases_screen.dart @@ -0,0 +1,200 @@ +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/utils/date_time_utils.dart'; +import 'package:eatvio_app/core/utils/localization_utils.dart'; +import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart'; +import 'package:eatvio_app/core/widgets/loader.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_cubit.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_state.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart'; +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class UserPurchasesScreen extends StatefulWidget { + const UserPurchasesScreen({super.key}); + static const name = 'userPurchasesScreen'; + static const path = '/user-purchases-screen'; + @override + State createState() => _UserPurchasesScreen(); +} + +class _UserPurchasesScreen extends State { + @override + Widget build(BuildContext context) { + final s = S.of(context); + return BlocProvider( + create: (context) => sl()..getUserPurchases(), + child: BlocBuilder( + builder: (context, state) { + return Scaffold( + backgroundColor: const Color(0xFFF0F2F5), + appBar: AppBar( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadiusDirectional.vertical( + bottom: Radius.circular(12), + ), + ), + title: Text( + 'Финансы', + style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), + ), + centerTitle: true, + ), + body: state.isLoading + ? const Center( + child: Loader(), + ) + : SingleChildScrollView( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Column( + children: [ + const SizedBox(height: 8), + Material( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + child: Padding( + padding: const EdgeInsets.only(left: 16, right: 16, top: 16), + child: SingleChildScrollView( + physics: const NeverScrollableScrollPhysics(), + child: Column( + children: [ + CustomDropDownButton( + items: PeriodFilterStatus.values + .map( + (value) => DropdownMenuItem( + value: value, + child: Text( + value.name, + style: TextStyles.textMMedium.copyWith(color: appTheme.black), + ), + ), + ) + .toList(), + onChanged: (value) { + if (value == null) return; + context.read().setPurchasesPeriod(value); + }, + value: state.periodFilterStatus ?? PeriodFilterStatus.values.first, + ), + const SizedBox(height: 12), + if (state.purchasesModels.isNotEmpty) + ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + return Material( + color: const Color(0xFFF6F6F6), + borderRadius: BorderRadius.circular(20), + child: Padding( + padding: const EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Builder(builder: (context) { + // final idLen = state.purchasesModels[index].bankId.length; + final bankId = state.purchasesModels[index].bankId; + return Text( + '$bankId', + style: TextStyles.textSMedium.copyWith(color: appTheme.black), + ); + }), + Text( + state.purchasesModels[index].startDate.format('yyyy.MM.dd'), + style: const TextStyle( + color: Color(0xFF6D6F71), + height: 1, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + const SizedBox(height: 8), + Text( + state.purchasesModels[index].title, + style: TextStyle( + color: appTheme.blueA400, + height: 1, + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 8), + Text( + 'Продавец: ${state.purchasesModels[index].consultantName}', + style: const TextStyle( + color: Color(0xFF323F4B), + height: 1, + fontSize: 14, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 8), + Text( + '${state.purchasesModels[index].duration} ${getDayForm(state.purchasesModels[index].duration, s)}', + style: const TextStyle( + color: Color(0xFF323F4B), + height: 1, + fontSize: 14, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 8), + Container( + height: 1, + width: double.infinity, + decoration: const BoxDecoration( + color: Color(0xFFDEDEDF), + ), + ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '${state.purchasesModels[index].paymentPrice} ₽', + style: const TextStyle( + color: Color(0xFF323F4B), + height: 1, + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + // StatusWidget( + // status: state.consultations[index].chosenPlanState, + // ), + ], + ), + ], + ), + ), + ); + }, + separatorBuilder: (context, index) => const SizedBox(height: 12), + itemCount: state.purchasesModels.length, + ) + else + const Padding( + padding: EdgeInsets.symmetric(vertical: 40.0), + child: Text( + 'У вас нет таких консультаций', + style: TextStyles.textMMedium, + ), + ), + ], + ), + ), + ), + ), + ], + )), + ); + }, + ), + ); + } +} diff --git a/lib/features/menu/presentation/pages/menu_screen.dart b/lib/features/menu/presentation/pages/menu_screen.dart index a20f34d0..8b3506ed 100644 --- a/lib/features/menu/presentation/pages/menu_screen.dart +++ b/lib/features/menu/presentation/pages/menu_screen.dart @@ -5,6 +5,7 @@ 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/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'; import 'package:eatvio_app/features/my_plans/presentation/my_plans_screen.dart'; import 'package:eatvio_app/features/profile/models/profile_model.dart'; @@ -43,8 +44,10 @@ class MenuScreen extends StatelessWidget { onTap: () { if (profileModel.roles.contains('consultant')) { GoRouter.of(context).push(ConsultantFinancesScreen.path); + GoRouter.of(context).push(UserPurchasesScreen.path); return; } + GoRouter.of(context).push(UserPurchasesScreen.path); }, ), _divider(context), diff --git a/lib/main.dart b/lib/main.dart index cb39ac46..b4eaba75 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -21,9 +21,9 @@ import 'package:sentry_flutter/sentry_flutter.dart'; Future main() async { WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); - FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); + FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); - await di.init(); + await di.init(); await SentryFlutter.init((options) { options.dsn = 'http://ba7f2653067776f3034e6e89850e85a1@sentry.md-desk.ru:9000/2'; options.tracesSampleRate = 1.0;