diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 2100a65d..d535af69 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -388,7 +388,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20; + CURRENT_PROJECT_VERSION = 21; DEVELOPMENT_TEAM = VL68C36HYS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -538,7 +538,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20; + CURRENT_PROJECT_VERSION = 21; DEVELOPMENT_TEAM = VL68C36HYS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -576,7 +576,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20; + CURRENT_PROJECT_VERSION = 21; DEVELOPMENT_TEAM = VL68C36HYS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index c23a16a5..498d4b70 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 20 + 21 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index 172d840d..fec9b100 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -32,7 +32,7 @@ import 'package:eatvio_app/features/glossary/presentation/pages/glossary_screen. import 'package:eatvio_app/features/menu/presentation/pages/menu_screen.dart'; import 'package:eatvio_app/features/my_diets/presentation/my_diets_screen.dart'; import 'package:eatvio_app/features/my_finance/presentation/pages/card_settings.dart'; -import 'package:eatvio_app/features/my_finance/presentation/pages/consultant_finance_screen.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/consultations_archive_screen.dart'; import 'package:eatvio_app/features/my_finance/presentation/pages/user_finance_screen.dart'; import 'package:eatvio_app/features/my_plans/presentation/my_plans_screen.dart'; import 'package:eatvio_app/features/notifications/presentation/notifications_screen.dart'; @@ -177,7 +177,7 @@ class AppRouter { GoRoute( path: '/consultant-finance-screen', name: 'consultantFinanceScreen', - builder: (context, state) => const ConsultantFinanceScreen(), + builder: (context, state) => const ConsultationsArchiveScreen(), ), GoRoute( path: '/user-finance-screen', diff --git a/lib/core/enums/filters_comparison_enum.dart b/lib/core/enums/filters_comparison_enum.dart new file mode 100644 index 00000000..57957552 --- /dev/null +++ b/lib/core/enums/filters_comparison_enum.dart @@ -0,0 +1,13 @@ +enum FiltersComparisonEnum { + equal('\$eq'), + notEqual('\$ne'), + greaterThan('\$gt'), + lessThan('\$lt'), + greaterThanOrEqual('\$gte'), + lessThanOrEqual('\$lte'), + inArray('\$in'), + notInArray('\$notIn'); + + final String value; + const FiltersComparisonEnum(this.value); +} diff --git a/lib/core/locator_service.dart b/lib/core/locator_service.dart index 1d44bc27..9ee53c7c 100644 --- a/lib/core/locator_service.dart +++ b/lib/core/locator_service.dart @@ -16,6 +16,7 @@ import 'package:eatvio_app/features/complaining/services/complaining_service.dar 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'; +import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/archive/consultations_archive_cubit.dart'; import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart'; import 'package:eatvio_app/features/create_diet/repositories/create_diet_repository.dart'; import 'package:eatvio_app/features/create_diet/services/create_diet_service.dart'; @@ -121,7 +122,8 @@ Future init() async { sl.registerFactory(() => PlansCubit(feedRepository: sl(), planRepository: sl())); sl.registerFactory(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl())); sl.registerFactory(() => RegisterConsultantCubit(consultantRepository: sl())); - sl.registerFactory(() => DiaryCubit(diaryRepository: sl(), glossaryRepository: sl(), profileRepository: sl(), dietRepository: sl())); + sl.registerFactory( + () => DiaryCubit(chatRepository: sl(), diaryRepository: sl(), glossaryRepository: sl(), profileRepository: sl(), dietRepository: sl())); sl.registerFactory(() => AboutDietCubit(dietsRepository: sl(), profileRepository: sl())); sl.registerFactory(() => BlogPostCreateCubit(blogPostRepository: sl())); sl.registerFactory(() => SubCubit(profileRepository: sl())); @@ -144,6 +146,7 @@ Future init() async { sl.registerFactory(() => CancelPlanCubit(cancelPlanRepository: sl())); sl.registerFactory(() => VerifyEmailCubit(profileRepository: sl())); sl.registerFactory(() => MyRecipesCubit(myRecipesRepository: sl(), aboutRecipeRepository: sl())); + sl.registerFactory(() => ConsultationsArchiveCubit(consultationsRepository: sl())); // Repository sl.registerLazySingleton(() => ChatRepository(chatService: sl())); diff --git a/lib/core/theme/theme_helper.dart b/lib/core/theme/theme_helper.dart index aa4e30a6..3b2a063d 100644 --- a/lib/core/theme/theme_helper.dart +++ b/lib/core/theme/theme_helper.dart @@ -101,8 +101,7 @@ class PrimaryColors { // Blue Color get blue400 => const Color(0XFF43A6F2); - Color get blueA400 => const Color(0XFF2F80ED); - + Color get blueA400 => const Color(0XFF2F80ED); // BlueGray Color get blueGray400 => const Color(0XFF7B8794); diff --git a/lib/features/consultations/presentation/pages/consultations_screen.dart b/lib/features/consultations/presentation/pages/consultations_screen.dart index 34f25b96..d6acb306 100644 --- a/lib/features/consultations/presentation/pages/consultations_screen.dart +++ b/lib/features/consultations/presentation/pages/consultations_screen.dart @@ -1,5 +1,6 @@ import 'package:eatvio_app/core/enums/base_state.dart'; import 'package:eatvio_app/core/locator_service.dart'; +import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/widgets/data_error_widget.dart'; import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_cubit.dart'; @@ -7,6 +8,7 @@ import 'package:eatvio_app/features/consultations/presentation/cubit/consultatio import 'package:eatvio_app/features/consultations/presentation/widgets/consultation_card_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; class ConsultationsScreen extends StatelessWidget { static const path = '/consultations-screen'; @@ -46,15 +48,49 @@ class ConsultationsContent extends StatelessWidget { context.read().loadConsultations(); }), BaseState.initial => const Loader(), - BaseState.success => ListView.separated( - padding: const EdgeInsets.symmetric(vertical: 12), - itemBuilder: (context, index) => ConsultationCardWidget( - consultationsModel: state.consultations[index], + BaseState.success => SingleChildScrollView( + child: Column( + children: [ + ListView.separated( + padding: const EdgeInsets.symmetric(vertical: 12), + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) => ConsultationCardWidget( + consultationsModel: state.consultations[index], + ), + separatorBuilder: (context, index) => const SizedBox( + height: 12, + ), + shrinkWrap: true, + itemCount: state.consultations.length, + ), + const SizedBox(height: 20), + InkWell( + borderRadius: BorderRadius.circular(20), + onTap: () { + GoRouter.of(context).pushNamed('consultantFinanceScreen'); + }, + child: Ink( + width: MediaQuery.of(context).size.width - 32, + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + color: appTheme.blueA400, + ), + child: const Center( + child: Text( + 'Архив консультаций', + style: TextStyle( + color: Colors.white, + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ), + const SizedBox(height: 20), + ], ), - separatorBuilder: (context, index) => const SizedBox( - height: 12, - ), - itemCount: state.consultations.length, ), }); }, diff --git a/lib/features/consultations/presentation/widgets/consultation_card_widget.dart b/lib/features/consultations/presentation/widgets/consultation_card_widget.dart index 834f0e58..0752847b 100644 --- a/lib/features/consultations/presentation/widgets/consultation_card_widget.dart +++ b/lib/features/consultations/presentation/widgets/consultation_card_widget.dart @@ -1,4 +1,5 @@ import 'package:collection/collection.dart'; +import 'package:eatvio_app/core/const.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_divider.dart'; @@ -73,7 +74,7 @@ class _ConsultationCardWidgetState extends State { children: [ CircleAvatar( radius: 30, - backgroundImage: NetworkImage(widget.consultationsModel.userProfilePhotoUrl), + backgroundImage: NetworkImage(baseUrl + widget.consultationsModel.userProfilePhotoUrl), ), const SizedBox(width: 12), Column( diff --git a/lib/features/consultations/repositories/consultations_repository.dart b/lib/features/consultations/repositories/consultations_repository.dart index 93d84f67..3ebcef87 100644 --- a/lib/features/consultations/repositories/consultations_repository.dart +++ b/lib/features/consultations/repositories/consultations_repository.dart @@ -1,5 +1,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/consultations/services/consultation_service.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/models/statistic_model.dart'; class ConsultationsRepository { final ConsultationService consultationService; @@ -13,4 +15,31 @@ class ConsultationsRepository { rethrow; } } + + Future> getFilteredConsultations({ + String? state, + String? date, + FiltersComparisonEnum? stateComparison, + FiltersComparisonEnum? dateComparison, + }) async { + try { + final model = await consultationService.getFilteredConsultations( + state: state, + date: date, + stateComparison: stateComparison, + dateComparison: dateComparison, + ); + return model.data; + } catch (e) { + rethrow; + } + } + + Future getStatistic() async { + try { + return await consultationService.getStatistics(); + } catch (e) { + rethrow; + } + } } diff --git a/lib/features/consultations/services/consultation_service.dart b/lib/features/consultations/services/consultation_service.dart index 49ebe41c..fa14b69c 100644 --- a/lib/features/consultations/services/consultation_service.dart +++ b/lib/features/consultations/services/consultation_service.dart @@ -1,17 +1,69 @@ import 'package:dio/dio.dart'; +import 'package:eatvio_app/core/enums/filters_comparison_enum.dart'; import 'package:eatvio_app/features/consultations/models/consultation_model.dart'; -import 'package:retrofit/retrofit.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/models/statistic_model.dart'; -part 'consultation_service.g.dart'; +class ConsultationService { + final Dio dio; + ConsultationService(this.dio); -@RestApi() -abstract class ConsultationService { - factory ConsultationService(Dio dio, {String baseUrl}) = _ConsultationService; + Future getConsultations() async { + try { + final response = await dio.get('/api/consultations', queryParameters: { + 'filters[last_state][\$notIn][]': const [ + 'initied', + 'in_coordination', + 'start_date_has_been_set', + 'pending_payment', + 'canceled_by_the_initiator', + 'charge_was_collected', + ], + 'sort[]': 'created_at:desc' + }); + return ConsultationPaginationModel.fromJson(response.data); + } catch (e) { + rethrow; + } + } - @GET('/api/consultations') - Future getConsultations({ - @Query('filters[last_state][\$notIn][]') String query = 'canceled_by_the_initiator', - @Query('filters[last_state][\$notIn][]') String query2 = 'charge_was_collected', - @Query('sort[]') String sort = 'created_at:desc', - }); + Future getFilteredConsultations({ + String? state, + String? date, + FiltersComparisonEnum? stateComparison, + FiltersComparisonEnum? dateComparison, + }) async { + try { + final response = await dio.get( + '/api/consultations', + queryParameters: { + if (stateComparison == FiltersComparisonEnum.notInArray) + 'filters[last_state][\$notIn][]': const [ + 'initied', + 'in_coordination', + 'start_date_has_been_set', + 'pending_payment', + 'canceled_by_the_initiator', + 'charge_was_collected', + ] + else if (state != null && stateComparison != null) + 'filters[last_state][${stateComparison.value}]': state, + if (dateComparison != null && date != null) 'filters[start_date][${dateComparison.value}]': date, + }, + ); + return ConsultationPaginationModel.fromJson(response.data); + } catch (e) { + rethrow; + } + } + + Future getStatistics() async { + try { + final response = await dio.get( + '/api/consultations/statistics', + ); + return StatisticModel.fromJson(response.data); + } catch (e) { + rethrow; + } + } } diff --git a/lib/features/diary/presentation/bloc/diary_cubit.dart b/lib/features/diary/presentation/bloc/diary_cubit.dart index a07f6611..425c95a7 100644 --- a/lib/features/diary/presentation/bloc/diary_cubit.dart +++ b/lib/features/diary/presentation/bloc/diary_cubit.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart'; import 'package:eatvio_app/core/utils/date_time_utils.dart'; +import 'package:eatvio_app/features/chat/repositories/chat_repository.dart'; import 'package:eatvio_app/features/consultations/models/consultation_model.dart'; import 'package:eatvio_app/features/diary/models/consultant_review_model.dart'; import 'package:eatvio_app/features/diary/models/diary_day_model.dart'; @@ -23,8 +24,14 @@ class DiaryCubit extends Cubit { final GlossaryRepository glossaryRepository; final ProfileRepository profileRepository; final DietRepository dietRepository; + final ChatRepository chatRepository; int? diaryId; - DiaryCubit({required this.diaryRepository, required this.glossaryRepository, required this.profileRepository, required this.dietRepository}) + DiaryCubit( + {required this.chatRepository, + required this.diaryRepository, + required this.glossaryRepository, + required this.profileRepository, + required this.dietRepository}) : super(const DiaryState.loading()) { today = DateTime.now(); selectedDay = today; @@ -146,6 +153,7 @@ class DiaryCubit extends Cubit { fetchDiary([bool needLoading = false, int? diaryId]) async { this.diaryId ??= diaryId; + if (needLoading) { emit(const DiaryState.loading()); selectedMeal = 1; @@ -159,8 +167,15 @@ class DiaryCubit extends Cubit { diaryDay = await diaryRepository.getDiaryByDate(stringDate); } ConsultantReviewModel? consultantReview; + int? planDuration; + int? currentPlanDay; + String? planName; if (diaryDay.chosenPlanId != null) { consultantReview = await diaryRepository.getDiaryComments(planId: diaryDay.chosenPlanId!, diaryId: diaryDay.id); + final chosenPlanModel = await chatRepository.getChosenPlan(id: diaryDay.chosenPlanId!); + planDuration = chosenPlanModel.plan.duration; + planName = chosenPlanModel.plan.name; + currentPlanDay = DateTime.now().difference(chosenPlanModel.startDate).inDays + 1; } final nutrition = await getNutrition(diaryDay.id); @@ -179,6 +194,9 @@ class DiaryCubit extends Cubit { consultantReview: consultantReview, isDiaryFinished: isDiaryFinished, isDiaryCommented: isDiaryCommented, + chosenPlanCurrentDay: currentPlanDay, + chosenPlanDuration: planDuration, + chosenPlanName: planName, ), ); } catch (e, st) { diff --git a/lib/features/diary/presentation/bloc/diary_state.dart b/lib/features/diary/presentation/bloc/diary_state.dart index 40f1f0ef..828831d3 100644 --- a/lib/features/diary/presentation/bloc/diary_state.dart +++ b/lib/features/diary/presentation/bloc/diary_state.dart @@ -20,8 +20,11 @@ abstract class DiaryState with _$DiaryState { required NutritionDataModel nutrition, required DateTime selectedDay, required int selectedMeal, - @Default(false) bool isConsultantView, + int? chosenPlanCurrentDay, + String? chosenPlanName, + int? chosenPlanDuration, ConsultantReviewModel? consultantReview, + @Default(false) bool isConsultantView, @Default(false) bool isDiaryFinished, @Default(false) bool isDiaryCommented, }) = DiaryStateLoaded; diff --git a/lib/features/diary/presentation/widgets/chosen_plan_widget.dart b/lib/features/diary/presentation/widgets/chosen_plan_widget.dart new file mode 100644 index 00000000..30ce656c --- /dev/null +++ b/lib/features/diary/presentation/widgets/chosen_plan_widget.dart @@ -0,0 +1,66 @@ +import 'package:eatvio_app/core/theme/custom_text_style.dart'; +import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart'; +import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart'; +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class ChosenPlanWidget extends StatelessWidget { + const ChosenPlanWidget({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder(builder: (context, state) { + if (state is DiaryStateLoaded) { + if (state.diary.typeOfDiary.hasChosenPlan) { + return ChosenPlanWidgetContent( + currentPlanDay: state.chosenPlanCurrentDay ?? 0, + planName: state.chosenPlanName ?? '', + planDuration: state.chosenPlanDuration ?? 0, + ); + } + } + + return const SizedBox.shrink(); + }); + } +} + +class ChosenPlanWidgetContent extends StatelessWidget { + const ChosenPlanWidgetContent({ + super.key, + required this.currentPlanDay, + required this.planName, + required this.planDuration, + }); + final int currentPlanDay; + final String planName; + final int planDuration; + @override + Widget build(BuildContext context) { + final size = MediaQuery.sizeOf(context); + final s = S.of(context); + return Column( + children: [ + Row( + children: [ + SizedBox( + width: size.width * 0.4, + child: Text(planName, style: TextStyles.textLMedium.copyWith(color: appTheme.black)), + ), + const Spacer(), + Container( + decoration: BoxDecoration(color: appTheme.dividerColor, borderRadius: BorderRadius.circular(12)), + padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12), + child: Text('${s.day} $currentPlanDay из $planDuration', style: TextStyles.textMSemibold.copyWith(color: appTheme.gray)), + ) + ], + ), + const SizedBox( + height: 16, + ) + ], + ); + } +} diff --git a/lib/features/diary/presentation/widgets/meal_widget.dart b/lib/features/diary/presentation/widgets/meal_widget.dart index dc34a7c0..b5bd196a 100644 --- a/lib/features/diary/presentation/widgets/meal_widget.dart +++ b/lib/features/diary/presentation/widgets/meal_widget.dart @@ -5,6 +5,7 @@ import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart'; import 'package:eatvio_app/features/diary/presentation/widgets/add_product_button_widget.dart'; import 'package:eatvio_app/features/diary/presentation/widgets/chosen_diet_widget.dart'; +import 'package:eatvio_app/features/diary/presentation/widgets/chosen_plan_widget.dart'; import 'package:eatvio_app/features/diary/presentation/widgets/comment_container.dart'; import 'package:eatvio_app/features/diary/presentation/widgets/meal_button_widget.dart'; import 'package:eatvio_app/features/diary/presentation/widgets/meal_comment_widget.dart'; @@ -60,6 +61,7 @@ class _MealWidgetState extends State { const TimeOfMealPickerWidget(), const ChosenDietWidget(), + const ChosenPlanWidget(), const ProductsListWidget(), if (widget.showAddButton) ...[ const SizedBox(height: 12), diff --git a/lib/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart b/lib/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart index 15b51e8f..17c435cb 100644 --- a/lib/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart +++ b/lib/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_state.dart'; import 'package:eatvio_app/features/feed/repositories/feed_repository.dart'; @@ -77,7 +79,13 @@ class FeedCubit extends Cubit { } } - likePost(int id) async => await feedRepository.likeBlogPost(id: id); + likePost(int id) async { + try { + await feedRepository.likeBlogPost(id: id); + } catch (e, st) { + log(e.toString(), stackTrace: st); + } + } unlikedPost(int id) async => await feedRepository.unlikedBlogPost(id: id); diff --git a/lib/features/finances_and_consultations_archive/cubit/archive/consultations_archive_cubit.dart b/lib/features/finances_and_consultations_archive/cubit/archive/consultations_archive_cubit.dart new file mode 100644 index 00000000..dd6c713e --- /dev/null +++ b/lib/features/finances_and_consultations_archive/cubit/archive/consultations_archive_cubit.dart @@ -0,0 +1,86 @@ +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/archive/consultations_archive_state.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/models/statistic_model.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class ConsultationsArchiveCubit extends Cubit { + final ConsultationsRepository consultationsRepository; + ConsultationsArchiveCubit({ + required this.consultationsRepository, + }) : super( + ConsultationsArchiveState( + statisticModel: StatisticModel( + total: 0, + canceled: 0, + inProgress: 0, + finished: 0, + ), + ), + ); + + Future getConsultations() async { + try { + emit(state.copyWith(isLoading: true)); + final consultations = await consultationsRepository.getFilteredConsultations( + date: state.periodFilterStatus?.value, + state: state.consultationFilterStatus?.value, + dateComparison: state.periodFilterStatus == PeriodFilterStatus.all ? null : FiltersComparisonEnum.greaterThanOrEqual, + stateComparison: state.consultationFilterStatus == ConsultationFilterStatus.all + ? null + : state.consultationFilterStatus == ConsultationFilterStatus.inProgress + ? FiltersComparisonEnum.notInArray + : FiltersComparisonEnum.equal, + ); + emit(state.copyWith(consultations: consultations, isLoading: false)); + } catch (e) { + emit(state.copyWith(isLoading: false)); + log(e.toString()); + } + } + + Future setConsultationsPeriod(PeriodFilterStatus period) async { + emit(state.copyWith(periodFilterStatus: period)); + await getConsultations(); + } + + Future setConsultationsStatus(ConsultationFilterStatus status) async { + emit(state.copyWith(consultationFilterStatus: status)); + await getConsultations(); + } + + Future getStatistic() async { + try { + emit(state.copyWith(isLoading: true)); + final statistic = await consultationsRepository.getStatistic(); + emit(state.copyWith(statisticModel: statistic, isLoading: false)); + } catch (e) { + emit(state.copyWith(isLoading: false)); + log(e.toString()); + } + } +} + +enum ConsultationFilterStatus { + all, + canceled, + finished, + inProgress; + + String get name => switch (this) { + ConsultationFilterStatus.all => 'Все консультации', + ConsultationFilterStatus.canceled => 'Отменён', + ConsultationFilterStatus.finished => 'Завершён', + ConsultationFilterStatus.inProgress => 'В процессе', + }; + + String? get value => switch (this) { + ConsultationFilterStatus.all => null, + ConsultationFilterStatus.canceled => 'canceled_by_the_initiator', + ConsultationFilterStatus.finished => 'charge_was_collected', + ConsultationFilterStatus.inProgress => 'active', + }; +} diff --git a/lib/features/finances_and_consultations_archive/cubit/archive/consultations_archive_state.dart b/lib/features/finances_and_consultations_archive/cubit/archive/consultations_archive_state.dart new file mode 100644 index 00000000..c54f8073 --- /dev/null +++ b/lib/features/finances_and_consultations_archive/cubit/archive/consultations_archive_state.dart @@ -0,0 +1,22 @@ + +import 'package:eatvio_app/features/consultations/models/consultation_model.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/archive/consultations_archive_cubit.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/models/statistic_model.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'consultations_archive_state.freezed.dart'; + +@freezed +abstract class ConsultationsArchiveState with _$ConsultationsArchiveState { + + factory ConsultationsArchiveState({ + @Default([]) List consultations, + @Default(false) isLoading, + @Default(null) String? error, + @Default(null) PeriodFilterStatus? periodFilterStatus, + @Default(null) ConsultationFilterStatus? consultationFilterStatus, + required StatisticModel statisticModel, + }) = _ConsultationsArchiveState; + +} \ No newline at end of file diff --git a/lib/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_cubit.dart b/lib/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_cubit.dart new file mode 100644 index 00000000..1324da92 --- /dev/null +++ b/lib/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_cubit.dart @@ -0,0 +1,31 @@ +import 'dart:developer'; + +import 'package:eatvio_app/features/consultations/repositories/consultations_repository.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_state.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class ConsultantFinancesCubit extends Cubit { + final ConsultationsRepository consultationsRepository; + ConsultantFinancesCubit({ + required this.consultationsRepository, + }) : super( + ConsultantFinancesState(), + ); + + Future getConsultations() async { + try { + emit(state.copyWith(isLoading: true)); + } catch (e) { + emit(state.copyWith(isLoading: false)); + log(e.toString()); + } + } + + Future setConsultationsPeriod(PeriodFilterStatus period) async { + emit(state.copyWith(periodFilterStatus: period)); + await getConsultations(); + } +} + + diff --git a/lib/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_state.dart b/lib/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_state.dart new file mode 100644 index 00000000..9692d4a6 --- /dev/null +++ b/lib/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_state.dart @@ -0,0 +1,15 @@ +import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'consultant_finances_state.freezed.dart'; + +@freezed +abstract class ConsultantFinancesState with _$ConsultantFinancesState { + factory ConsultantFinancesState({ + @Default([]) List financesModels, + @Default(false) isLoading, + @Default(null) String? error, + @Default(null) PeriodFilterStatus? periodFilterStatus, + }) = _ConsultantFinancesState; + +} diff --git a/lib/features/finances_and_consultations_archive/models/statistic_model.dart b/lib/features/finances_and_consultations_archive/models/statistic_model.dart new file mode 100644 index 00000000..34cc748a --- /dev/null +++ b/lib/features/finances_and_consultations_archive/models/statistic_model.dart @@ -0,0 +1,16 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'statistic_model.freezed.dart'; +part 'statistic_model.g.dart'; + +@freezed +abstract class StatisticModel with _$StatisticModel { + factory StatisticModel({ + required int total, + @JsonKey(name: 'in_progress') required int inProgress, + required int finished, + required int canceled, + }) = _StatisticModel; + + factory StatisticModel.fromJson(Map json) => _$StatisticModelFromJson(json); +} \ No newline at end of file diff --git a/lib/features/finances_and_consultations_archive/period_enum.dart b/lib/features/finances_and_consultations_archive/period_enum.dart new file mode 100644 index 00000000..a39489d9 --- /dev/null +++ b/lib/features/finances_and_consultations_archive/period_enum.dart @@ -0,0 +1,19 @@ +import 'package:eatvio_app/core/utils/date_time_utils.dart'; + +enum PeriodFilterStatus { + all, + month, + week; + + String get name => switch (this) { + PeriodFilterStatus.all => 'Все периоды', + PeriodFilterStatus.month => 'За месяц', + PeriodFilterStatus.week => 'За неделю', + }; + + 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'), + }; +} \ 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 new file mode 100644 index 00000000..bb225221 --- /dev/null +++ b/lib/features/finances_and_consultations_archive/presentation/consultant_finances_screen.dart @@ -0,0 +1,281 @@ +// 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/archive/consultations_archive_cubit.dart'; +// import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/archive/consultations_archive_state.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/consultant_finances/consultant_finances_state.dart'; +// import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart'; +// import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart'; +// import 'package:eatvio_app/generated/l10n.dart'; +// import 'package:flutter/material.dart'; +// import 'package:flutter_bloc/flutter_bloc.dart'; + +// class ConsultantFinancesScreen extends StatefulWidget { +// const ConsultantFinancesScreen({super.key}); + +// @override +// State createState() => _ConsultantFinancesScreenState(); +// } + +// class _ConsultantFinancesScreenState extends State { +// @override +// Widget build(BuildContext context) { +// final s = S.of(context); +// return BlocProvider( +// create: (context) => sl(), +// child: BlocBuilder( +// builder: (context, state) { +// return Scaffold( +// backgroundColor: const Color(0xFFF0F2F5), +// appBar: AppBar( +// shape: const RoundedRectangleBorder( +// borderRadius: BorderRadiusDirectional.vertical( +// bottom: Radius.circular(12), +// ), +// ), +// title: const Text('Архив консультаций'), +// centerTitle: true, +// ), +// body: state.isLoading +// ? const Center( +// child: Loader(), +// ) +// : SingleChildScrollView( +// padding: const EdgeInsets.symmetric(vertical: 8), +// child: Column( +// children: [ +// Material( +// color: Colors.white, +// borderRadius: BorderRadius.circular(12), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// const SizedBox(height: 16), +// Padding( +// padding: const EdgeInsets.symmetric(horizontal: 16), +// child: Text( +// 'Всего консультаций: ${state.statisticModel.total}', +// style: const TextStyle( +// fontSize: 16, +// fontWeight: FontWeight.w500, +// color: Color(0xFF6D6F71), +// height: 1, +// ), +// ), +// ), +// const SizedBox(height: 12), +// Row( +// children: [ +// const SizedBox(width: 16), +// Material( +// color: const Color(0xFF0BB450).withAlpha(11), +// borderRadius: BorderRadius.circular(8), +// child: Padding( +// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), +// child: Text( +// 'Завершено: ${state.statisticModel.finished}', +// style: const TextStyle( +// fontSize: 14, +// fontWeight: FontWeight.w400, +// color: Color(0xFF0BB450), +// height: 1, +// ), +// ), +// ), +// ), +// const SizedBox(width: 8), +// Material( +// color: const Color(0xFFD01717).withAlpha(11), +// borderRadius: BorderRadius.circular(8), +// child: Padding( +// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), +// child: Text( +// 'Отменено: ${state.statisticModel.canceled}', +// style: const TextStyle( +// fontSize: 14, +// fontWeight: FontWeight.w400, +// color: Color(0xFFD01717), +// height: 1, +// ), +// ), +// ), +// ), +// ], +// ), +// const SizedBox(height: 16), +// ], +// ), +// ), +// 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().setConsultationsPeriod(value); +// }, +// value: state.periodFilterStatus ?? PeriodFilterStatus.values.first, +// ), +// const SizedBox(height: 12), +// CustomDropDownButton( +// items: ConsultationFilterStatus.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().setConsultationsStatus(value); +// }, +// value: state.consultationFilterStatus, +// ), +// const SizedBox(height: 12), +// if (state.consultations.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: [ +// Text( +// state.consultations[index].id.toString(), +// style: const TextStyle( +// color: Color(0xFF323F4B), +// height: 1, +// fontWeight: FontWeight.w500, +// ), +// ), +// Text( +// state.consultations[index].startDate.format('yyyy.MM.dd'), +// style: const TextStyle( +// color: Color(0xFF6D6F71), +// height: 1, +// fontWeight: FontWeight.w500, +// ), +// ), +// ], +// ), +// const SizedBox(height: 8), +// Text( +// state.consultations[index].planTitle, +// style: TextStyle( +// color: appTheme.blueA400, +// height: 1, +// fontSize: 18, +// fontWeight: FontWeight.w600, +// ), +// ), +// const SizedBox(height: 8), +// Text( +// 'Покупатель: ${state.consultations[index].userName}', +// style: const TextStyle( +// color: Color(0xFF323F4B), +// height: 1, +// fontSize: 14, +// fontWeight: FontWeight.w500, +// ), +// ), +// const SizedBox(height: 8), +// Text( +// '${state.consultations[index].duration} ${getDayForm(state.consultations[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.consultations[index].consultantPayment.toStringAsFixed(2)} ₽', +// 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.financesModels.length, +// ) +// else +// const Padding( +// padding: EdgeInsets.symmetric(vertical: 40.0), +// child: Text( +// 'У вас нет таких консультаций', +// style: TextStyles.textMMedium, +// ), +// ), +// ], +// ), +// ), +// ), +// ), +// ], +// ), +// ), +// ); +// }, +// ), +// ); +// } +// } diff --git a/lib/features/finances_and_consultations_archive/presentation/consultations_archive_screen.dart b/lib/features/finances_and_consultations_archive/presentation/consultations_archive_screen.dart new file mode 100644 index 00000000..fa934bcf --- /dev/null +++ b/lib/features/finances_and_consultations_archive/presentation/consultations_archive_screen.dart @@ -0,0 +1,281 @@ +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/archive/consultations_archive_cubit.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/archive/consultations_archive_state.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart'; +import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart'; +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class ConsultationsArchiveScreen extends StatefulWidget { + const ConsultationsArchiveScreen({super.key}); + + @override + State createState() => _ConsultationsArchiveScreenState(); +} + +class _ConsultationsArchiveScreenState extends State { + @override + Widget build(BuildContext context) { + final s = S.of(context); + return BlocProvider( + create: (context) => sl() + ..getConsultations() + ..getStatistic(), + child: BlocBuilder( + builder: (context, state) { + return Scaffold( + backgroundColor: const Color(0xFFF0F2F5), + appBar: AppBar( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadiusDirectional.vertical( + bottom: Radius.circular(12), + ), + ), + title: const Text('Архив консультаций'), + centerTitle: true, + ), + body: state.isLoading + ? const Center( + child: Loader(), + ) + : SingleChildScrollView( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Column( + children: [ + Material( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 16), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Text( + 'Всего консультаций: ${state.statisticModel.total}', + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Color(0xFF6D6F71), + height: 1, + ), + ), + ), + const SizedBox(height: 12), + Row( + children: [ + const SizedBox(width: 16), + Material( + color: const Color(0xFF0BB450).withAlpha(11), + borderRadius: BorderRadius.circular(8), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), + child: Text( + 'Завершено: ${state.statisticModel.finished}', + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: Color(0xFF0BB450), + height: 1, + ), + ), + ), + ), + const SizedBox(width: 8), + Material( + color: const Color(0xFFD01717).withAlpha(11), + borderRadius: BorderRadius.circular(8), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), + child: Text( + 'Отменено: ${state.statisticModel.canceled}', + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: Color(0xFFD01717), + height: 1, + ), + ), + ), + ), + ], + ), + const SizedBox(height: 16), + ], + ), + ), + 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().setConsultationsPeriod(value); + }, + value: state.periodFilterStatus ?? PeriodFilterStatus.values.first, + ), + const SizedBox(height: 12), + CustomDropDownButton( + items: ConsultationFilterStatus.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().setConsultationsStatus(value); + }, + value: state.consultationFilterStatus ?? ConsultationFilterStatus.values.first, + ), + const SizedBox(height: 12), + if (state.consultations.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: [ + Text( + state.consultations[index].id.toString(), + style: const TextStyle( + color: Color(0xFF323F4B), + height: 1, + fontWeight: FontWeight.w500, + ), + ), + Text( + state.consultations[index].startDate.format('yyyy.MM.dd'), + style: const TextStyle( + color: Color(0xFF6D6F71), + height: 1, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + const SizedBox(height: 8), + Text( + state.consultations[index].planTitle, + style: TextStyle( + color: appTheme.blueA400, + height: 1, + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 8), + Text( + 'Покупатель: ${state.consultations[index].userName}', + style: const TextStyle( + color: Color(0xFF323F4B), + height: 1, + fontSize: 14, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 8), + Text( + '${state.consultations[index].duration} ${getDayForm(state.consultations[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.consultations[index].consultantPayment.toStringAsFixed(2)} ₽', + 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.consultations.length, + ) + else + const Padding( + padding: EdgeInsets.symmetric(vertical: 40.0), + child: Text( + 'У вас нет таких консультаций', + style: TextStyles.textMMedium, + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ); + }, + ), + ); + } +} diff --git a/lib/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart b/lib/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart new file mode 100644 index 00000000..d53c254a --- /dev/null +++ b/lib/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart @@ -0,0 +1,63 @@ +import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart'; +import 'package:flutter/material.dart'; + +class StatusWidget extends StatelessWidget { + const StatusWidget({super.key, required this.status}); + final ChosenPlanStateEnum status; + + @override + Widget build(BuildContext context) { + if (status == ChosenPlanStateEnum.stateCanceledByTheInitiator) { + return Material( + color: appTheme.lightRed, + borderRadius: BorderRadius.circular(8), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), + child: Text( + 'Отменён', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: appTheme.strongRed, + height: 1, + ), + ), + ), + ); + } else if (status == ChosenPlanStateEnum.stateChargeWasCollected) { + return Material( + color: const Color(0xFF0BB450).withAlpha(11), + borderRadius: BorderRadius.circular(8), + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4), + child: Text( + 'Успешно', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: Color(0xFF0BB450), + height: 1, + ), + ), + ), + ); + } + return Material( + color: appTheme.transparentBlue, + borderRadius: BorderRadius.circular(8), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), + child: Text( + 'В процессе', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: appTheme.blueA400, + height: 1, + ), + ), + ), + ); + } +} diff --git a/lib/features/my_finance/presentation/pages/consultant_finance_screen.dart b/lib/features/my_finance/presentation/pages/consultant_finance_screen.dart deleted file mode 100644 index 3bd9dc47..00000000 --- a/lib/features/my_finance/presentation/pages/consultant_finance_screen.dart +++ /dev/null @@ -1,259 +0,0 @@ -import 'package:collection/collection.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/widgets/custom_drop_down_button.dart'; -import 'package:eatvio_app/features/my_finance/entities/consultant_finance_entity.dart'; -import 'package:flutter/material.dart'; - -class ConsultantFinanceScreen extends StatefulWidget { - const ConsultantFinanceScreen({super.key}); - - @override - State createState() => _ConsultantFinanceScreenState(); -} - -class _ConsultantFinanceScreenState extends State { - final List periods = ['За всё время', 'За месяц', 'За неделю']; - final List statuses = ['Все консультации', 'Отменён', 'Завершён', 'В процессе']; - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: const Color(0xFFF0F2F5), - appBar: AppBar( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadiusDirectional.vertical( - bottom: Radius.circular(12), - ), - ), - title: const Text('Архив консультаций'), - centerTitle: true, - ), - body: SingleChildScrollView( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Column( - children: [ - Material( - color: Colors.white, - borderRadius: BorderRadius.circular(12), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 16), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - 'Всего консультаций: ${testFinanceEntity.totalConsultationsCount}', - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Color(0xFF6D6F71), - height: 1, - ), - ), - ), - const SizedBox(height: 12), - Row( - children: [ - const SizedBox(width: 16), - Material( - color: const Color(0xFF0BB450).withAlpha(11), - borderRadius: BorderRadius.circular(8), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), - child: Text( - 'Завершено: ${testFinanceEntity.completedCount}', - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w400, - color: Color(0xFF0BB450), - height: 1, - ), - ), - ), - ), - const SizedBox(width: 8), - Material( - color: const Color(0xFFD01717).withAlpha(11), - borderRadius: BorderRadius.circular(8), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), - child: Text( - 'Отменено: ${testFinanceEntity.canceledCount}', - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w400, - color: Color(0xFFD01717), - height: 1, - ), - ), - ), - ), - ], - ), - const SizedBox(height: 16), - ], - ), - ), - 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: periods - .mapIndexed( - (index, value) => DropdownMenuItem( - value: periods[index], - child: Text( - periods[index], - style: TextStyles.textMMedium.copyWith(color: appTheme.black), - ), - ), - ) - .toList(), - onChanged: (value) {}, - value: periods.first, - ), - const SizedBox(height: 12), - CustomDropDownButton( - items: statuses - .mapIndexed( - (index, value) => DropdownMenuItem( - value: statuses[index], - child: Text( - statuses[index], - style: TextStyles.textMMedium.copyWith(color: appTheme.black), - ), - ), - ) - .toList(), - onChanged: (value) {}, - value: statuses.first, - ), - const SizedBox(height: 12), - ListView.separated( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, index) => 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: [ - Text( - testFinanceEntity.consultationsList[index].id, - style: const TextStyle( - color: Color(0xFF323F4B), - height: 1, - fontWeight: FontWeight.w500, - ), - ), - Text( - testFinanceEntity.consultationsList[index].date.format('dd.MM.yyy', 'RU'), - style: const TextStyle( - color: Color(0xFF6D6F71), - height: 1, - fontWeight: FontWeight.w500, - ), - ), - ], - ), - const SizedBox(height: 8), - Text( - testFinanceEntity.consultationsList[index].title, - style: TextStyle( - color: appTheme.blueA400, - height: 1, - fontSize: 18, - fontWeight: FontWeight.w600, - ), - ), - const SizedBox(height: 8), - Text( - 'Покупатель: ${testFinanceEntity.consultationsList[index].clientName}', - style: const TextStyle( - color: Color(0xFF323F4B), - height: 1, - fontSize: 14, - fontWeight: FontWeight.w500, - ), - ), - const SizedBox(height: 8), - Text( - '${testFinanceEntity.consultationsList[index].planDuration} дней', - 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( - '${testFinanceEntity.consultationsList[index].price} ₽', - style: const TextStyle( - color: Color(0xFF323F4B), - height: 1, - fontSize: 18, - fontWeight: FontWeight.w600, - ), - ), - Material( - color: const Color(0xFF0BB450).withAlpha(11), - borderRadius: BorderRadius.circular(8), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), - child: Text( - testFinanceEntity.consultationsList[index].status.name, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w400, - color: Color(0xFF0BB450), - height: 1, - ), - ), - ), - ), - ], - ), - ], - ), - ), - ), - separatorBuilder: (context, index) => const SizedBox(height: 12), - itemCount: testFinanceEntity.consultationsList.length, - ), - ], - ), - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/features/plans/enums/chosen_plan_state_enum.dart b/lib/features/plans/enums/chosen_plan_state_enum.dart index 308df73c..0b768d5f 100644 --- a/lib/features/plans/enums/chosen_plan_state_enum.dart +++ b/lib/features/plans/enums/chosen_plan_state_enum.dart @@ -55,4 +55,16 @@ enum ChosenPlanStateEnum { return true; } } + + static List getActiveStates() { + return [ + ChosenPlanStateEnum.statePaid, + ChosenPlanStateEnum.stateStarted, + ChosenPlanStateEnum.stateInProgress, + ChosenPlanStateEnum.stateLastDayStarted, + ChosenPlanStateEnum.stateLastDayFinished, + ChosenPlanStateEnum.stateComplained, + ChosenPlanStateEnum.stateDone + ]; + } } diff --git a/lib/features/profile/presentation/pages/profile_screen.dart b/lib/features/profile/presentation/pages/profile_screen.dart index 64c7dda7..d5d360d8 100644 --- a/lib/features/profile/presentation/pages/profile_screen.dart +++ b/lib/features/profile/presentation/pages/profile_screen.dart @@ -562,7 +562,7 @@ class _ProfileScreenState extends State { color: Colors.white, borderRadius: BorderRadius.circular(12), child: Padding( - padding: const EdgeInsets.only(left: 16, right: 16, top: 20), + padding: const EdgeInsets.all(16), child: Column( children: [ ListView.separated( @@ -964,7 +964,7 @@ class _ProfileScreenState extends State { context.pushTransparentRoute( rootNavigator: true, PhotoViewScreen( - imageUrls: [state.profile.profilePhotoUrl], + imageUrls: [baseUrl + state.profile.profilePhotoUrl], index: 0, ), );