From 563d84ca12482ad50c9f3a3fdc94fb1367fd92fc Mon Sep 17 00:00:00 2001 From: misha Date: Wed, 29 Oct 2025 04:38:04 +0300 Subject: [PATCH] feat(InfoByOrder): added user info to InfoByOrderScreen author card --- lib/core/di/cubit_registrations.dart | 2 +- lib/core/routing/other_routes.dart | 3 +- .../chat/presentation/pages/chat_screen.dart | 15 ++- .../presentation/widgets/author_card.dart | 50 ++++++-- .../order_info/cubit/order_info_cubit.dart | 11 +- .../order_info/cubit/order_info_state.dart | 2 + .../presentation/info_by_order_screen.dart | 116 ++++++------------ lib/generated/intl/messages_en.dart | 1 + lib/generated/l10n.dart | 12 +- lib/l10n/intl_en.arb | 3 +- 10 files changed, 108 insertions(+), 107 deletions(-) diff --git a/lib/core/di/cubit_registrations.dart b/lib/core/di/cubit_registrations.dart index ca3ece97..605ff8a3 100644 --- a/lib/core/di/cubit_registrations.dart +++ b/lib/core/di/cubit_registrations.dart @@ -130,7 +130,7 @@ class CubitRegistrations { sl.registerFactory(() => RegisterConsultantCubit(consultantRepository: sl())); sl.registerFactory(() => ComplainingCubit(repository: sl())); sl.registerFactory(() => UserCardsCubit(cardsRepository: sl())); - sl.registerFactory(() => OrderInfoCubit(chatRepository: sl())); + sl.registerFactory(() => OrderInfoCubit(chatRepository: sl(), profileRepository: sl())); sl.registerFactory(() => ConsultationsCubit(consultationsRepository: sl())); sl.registerFactory(() => ConsultantCommentsCubit(diaryRepository: sl())); sl.registerFactory(() => CancelPlanCubit(cancelPlanRepository: sl())); diff --git a/lib/core/routing/other_routes.dart b/lib/core/routing/other_routes.dart index 27c2e0cb..a5809c8f 100644 --- a/lib/core/routing/other_routes.dart +++ b/lib/core/routing/other_routes.dart @@ -215,7 +215,8 @@ class OtherRoutes { final data = state.extra as Map; final planId = data['planId'] as int; final successfulPayment = data['successfulPayment'] as bool?; - return InfoByOrderScreen(planId: planId, successfulPayment: successfulPayment); + final customerSlug = data['customerSlug'] as String?; + return InfoByOrderScreen(planId: planId, successfulPayment: successfulPayment, customerSlug: customerSlug); }, ), GoRoute( diff --git a/lib/features/chat/presentation/pages/chat_screen.dart b/lib/features/chat/presentation/pages/chat_screen.dart index c22ddbd3..8ac88169 100644 --- a/lib/features/chat/presentation/pages/chat_screen.dart +++ b/lib/features/chat/presentation/pages/chat_screen.dart @@ -149,10 +149,7 @@ class _ChatScreenState extends State { centerTitle: false, title: GestureDetector( onTap: () { - GoRouter.of(context).pushNamed( - 'profileScreen', - extra: state.chatRoom.users.receiver.slug, - ); + GoRouter.of(context).pushNamed('profileScreen', extra: state.chatRoom.users.receiver.slug); }, child: Row( children: [ @@ -447,7 +444,7 @@ class _ChatScreenState extends State { final blockedUsersCubit = sl(); await blockedUsersCubit.getBlockedUsers(); final blockedState = blockedUsersCubit.state; - + if (blockedState is BlockedUsersStateLoaded) { final isBlocked = blockedState.profilesList.any((profile) => profile.slug == state.chatRoom.users.receiver.slug); if (isBlocked) { @@ -462,7 +459,7 @@ class _ChatScreenState extends State { return; } } - + if (textEditingController.text.isNotEmpty || pickedFiles.isNotEmpty) { chatRoomCubit.sendMessage( data: textEditingController.text.isEmpty ? null : textEditingController.text.trim(), @@ -497,6 +494,12 @@ class _ChatScreenState extends State { left: 16, child: AboutOrderButton( onTap: () async { + if (amIConsultant) { + GoRouter.of( + context, + ).push(InfoByOrderScreen.path, extra: {'planId': state.chatRoom.chosenPlan?.id, 'customerSlug': state.chatRoom.users.receiver.slug}); + return; + } GoRouter.of(context).push(InfoByOrderScreen.path, extra: {'planId': state.chatRoom.chosenPlan?.id}); }, ), diff --git a/lib/features/diets/presentation/widgets/author_card.dart b/lib/features/diets/presentation/widgets/author_card.dart index be7d0736..3be70bce 100644 --- a/lib/features/diets/presentation/widgets/author_card.dart +++ b/lib/features/diets/presentation/widgets/author_card.dart @@ -4,22 +4,24 @@ import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart' import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/features/profile/models/profile_model.dart'; +import 'package:eatvio_app/features/user_details/presentation/user_details_screen.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:go_router/go_router.dart'; class AuthorCard extends StatelessWidget { - const AuthorCard({super.key, required this.author, required this.title, this.backgroundColor, this.titleStyle}); + const AuthorCard({super.key, required this.author, required this.title, this.backgroundColor, this.titleStyle, this.customerProfile}); final Author author; final String title; final Color? backgroundColor; final TextStyle? titleStyle; - + final ProfileModel? customerProfile; @override Widget build(BuildContext context) { return GestureDetector( onTap: () { - GoRouter.of(context).pushNamed('profileScreen', extra: author.authorSlug); + GoRouter.of(context).pushNamed('profileScreen', extra: customerProfile != null ? customerProfile!.slug : author.authorSlug); }, child: Container( width: MediaQuery.sizeOf(context).width, @@ -39,24 +41,48 @@ class AuthorCard extends StatelessWidget { shape: BoxShape.circle, image: DecorationImage( image: NetworkImage( - author.authorProfileIcon.contains('https') ? author.authorProfileIcon : sl().baseUrl + author.authorProfileIcon, + customerProfile != null + ? customerProfile!.profilePhotoUrl.contains('https') + ? customerProfile!.profilePhotoUrl + : sl().baseUrl + customerProfile!.profilePhotoUrl + : author.authorProfileIcon.contains('https') + ? author.authorProfileIcon + : sl().baseUrl + author.authorProfileIcon, ), fit: BoxFit.cover, ), ), ), - subtitle: Text(author.authorName, style: TextStyles.textLSemibold.copyWith(color: appTheme.black)), + subtitle: Text( + customerProfile != null ? customerProfile!.name : author.authorName, + style: TextStyles.textLSemibold.copyWith(color: appTheme.black), + ), title: Text(title, style: titleStyle ?? TextStyles.textSSemibold.copyWith(color: appTheme.gray)), ), Row( children: [ - Text('Консультант', style: TextStyles.textMMedium.copyWith(color: appTheme.black)), - const SizedBox(width: 10), - if (author.authorPlaceInRating != null) - InfoBouble( - radius: 12, - backgroundColor: appTheme.whiteA700, - children: [Text('${author.authorPlaceInRating}-й в рейтинге', style: TextStyles.textMMediumAuto.copyWith(color: appTheme.blueA400))], + if (customerProfile == null) ...[ + Text('Консультант', style: TextStyles.textMMedium.copyWith(color: appTheme.black)), + const SizedBox(width: 10), + + if (author.authorPlaceInRating != null) + InfoBouble( + radius: 12, + backgroundColor: appTheme.whiteA700, + children: [ + Text('${author.authorPlaceInRating}-й в рейтинге', style: TextStyles.textMMediumAuto.copyWith(color: appTheme.blueA400)), + ], + ), + ] else + GestureDetector( + onTap: () { + GoRouter.of(context).pushNamed(UserDetailsScreen.name, extra: customerProfile!.slug); + }, + child: InfoBouble( + radius: 12, + backgroundColor: appTheme.whiteA700, + children: [Text('Анкета пользователя', style: TextStyles.textMMediumAuto.copyWith(color: appTheme.blueA400))], + ), ), ], ), diff --git a/lib/features/order_info/cubit/order_info_cubit.dart b/lib/features/order_info/cubit/order_info_cubit.dart index cc98f13d..af934fab 100644 --- a/lib/features/order_info/cubit/order_info_cubit.dart +++ b/lib/features/order_info/cubit/order_info_cubit.dart @@ -1,12 +1,13 @@ import 'dart:developer'; - import 'package:eatvio_app/features/chat/repositories/chat_repository.dart'; import 'package:eatvio_app/features/order_info/cubit/order_info_state.dart'; +import 'package:eatvio_app/features/profile/repositories/profile_repository.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class OrderInfoCubit extends Cubit { - OrderInfoCubit({required this.chatRepository}) : super(OrderInfoState()); + OrderInfoCubit({required this.chatRepository, required this.profileRepository}) : super(OrderInfoState()); final ChatRepository chatRepository; + final ProfileRepository profileRepository; Future getPaymentLink() async { try { @@ -20,10 +21,14 @@ class OrderInfoCubit extends Cubit { } } - Future getChosenPlan(int id) async { + Future getChosenPlan({required int id, String? customerSlug}) async { try { emit(state.copyWith(isLoading: true)); final chosenPlan = await chatRepository.getChosenPlan(id: id); + if (customerSlug != null) { + final profile = await profileRepository.getProfileBySlug(customerSlug); + emit(state.copyWith(customerProfile: profile)); + } emit(state.copyWith(isLoading: false, chosenPlan: chosenPlan)); } catch (e) { log(e.toString()); diff --git a/lib/features/order_info/cubit/order_info_state.dart b/lib/features/order_info/cubit/order_info_state.dart index df822417..5f438240 100644 --- a/lib/features/order_info/cubit/order_info_state.dart +++ b/lib/features/order_info/cubit/order_info_state.dart @@ -1,4 +1,5 @@ import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart'; +import 'package:eatvio_app/features/profile/models/profile_model.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'order_info_state.freezed.dart'; @@ -9,6 +10,7 @@ abstract class OrderInfoState with _$OrderInfoState { factory OrderInfoState({ @Default(true) bool isLoading, @Default(null) ChosenPlanModel? chosenPlan, + @Default(null) ProfileModel? customerProfile, }) = _OrderInfoState; } diff --git a/lib/features/order_info/presentation/info_by_order_screen.dart b/lib/features/order_info/presentation/info_by_order_screen.dart index a1d94580..8fc20639 100644 --- a/lib/features/order_info/presentation/info_by_order_screen.dart +++ b/lib/features/order_info/presentation/info_by_order_screen.dart @@ -24,31 +24,24 @@ import 'package:go_router/go_router.dart'; class InfoByOrderScreen extends StatelessWidget { static const name = 'infoByOrderScreen'; static const path = '/info-by-order-screen'; - const InfoByOrderScreen({super.key, this.successfulPayment, required this.planId}); + const InfoByOrderScreen({super.key, this.successfulPayment, required this.planId, this.customerSlug}); final bool? successfulPayment; final int planId; + final String? customerSlug; @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => sl()..getChosenPlan(planId), - child: InfoByOrderScreenContent( - successfulPayment: successfulPayment, - planId: planId, - ), + create: (context) => sl()..getChosenPlan(id: planId, customerSlug: customerSlug), + child: InfoByOrderScreenContent(successfulPayment: successfulPayment, planId: planId, customerSlug: customerSlug), ); } } -class InfoByOrderScreenContent extends StatefulWidget { - static const name = 'infoByOrderScreen'; - static const path = '/info-by-order-screen'; - const InfoByOrderScreenContent({ - super.key, - required this.planId, - required this.successfulPayment, - }); +class InfoByOrderScreenContent extends StatefulWidget { + const InfoByOrderScreenContent({super.key, required this.planId, required this.successfulPayment, this.customerSlug}); final int planId; final bool? successfulPayment; + final String? customerSlug; @override State createState() => _InfoByOrderScreenContentState(); } @@ -64,39 +57,30 @@ class _InfoByOrderScreenContentState extends State { if (state.isLoading) { return Scaffold( appBar: AppBar( - title: Text( - s.infoAboutOrder, - style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), - ), + title: Text(s.infoAboutOrder, style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black)), leading: IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () { - GoRouter.of(context).pop(); - }), - ), - body: const Center( - child: Loader(), + icon: const Icon(Icons.arrow_back), + onPressed: () { + GoRouter.of(context).pop(); + }, + ), ), + body: const Center(child: Loader()), ); } if (plan == null) { return Scaffold( appBar: AppBar( - title: Text( - s.infoAboutOrder, - style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), - ), + title: Text(s.infoAboutOrder, style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black)), leading: IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () { - GoRouter.of(context).pop(); - }), + icon: const Icon(Icons.arrow_back), + onPressed: () { + GoRouter.of(context).pop(); + }, + ), ), body: Center( - child: Text( - s.sorry_we_did_not_find_the_plan, - style: TextStyles.textSSemibold.copyWith(color: appTheme.black), - ), + child: Text(s.sorry_we_did_not_find_the_plan, style: TextStyles.textSSemibold.copyWith(color: appTheme.black)), ), ); } @@ -122,58 +106,35 @@ class _InfoByOrderScreenContentState extends State { return Scaffold( backgroundColor: appTheme.gray100, appBar: AppBar( - title: Text( - s.infoAboutOrder, - style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), - ), + title: Text(s.infoAboutOrder, style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black)), leading: IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () { - GoRouter.of(context).pop(); - }), + icon: const Icon(Icons.arrow_back), + onPressed: () { + GoRouter.of(context).pop(); + }, + ), ), body: SingleChildScrollView( child: Column( children: [ - if (plan.chosenPlanState == ChosenPlanStateEnum.stateDone) ...[ - CollectChargeBanner( - chosenPlan: plan, - price: chosenOption?.price ?? 0, - ) - ], - if (widget.successfulPayment != null) ...[ - const SizedBox(height: 8), - PaymentBanner( - successfulPayment: widget.successfulPayment ?? false, - ), - ], + if (plan.chosenPlanState == ChosenPlanStateEnum.stateDone) ...[CollectChargeBanner(chosenPlan: plan, price: chosenOption?.price ?? 0)], + if (widget.successfulPayment != null) ...[const SizedBox(height: 8), PaymentBanner(successfulPayment: widget.successfulPayment ?? false)], const SizedBox(height: 8), AuthorCard( author: plan.plan.author, - title: s.plan_seller, + title: state.customerProfile != null ? s.customer : s.plan_seller, titleStyle: TextStyles.textSSemibold.copyWith(color: appTheme.gray), backgroundColor: appTheme.whiteA700, + customerProfile: state.customerProfile, ), const SizedBox(height: 8), - PlanProps( - planDuration: plan.plan.duration, - planPrice: chosenOption?.price.toDouble() ?? 0, - planStatus: planStatus), + PlanProps(planDuration: plan.plan.duration, planPrice: chosenOption?.price.toDouble() ?? 0, planStatus: planStatus), const SizedBox(height: 8), - PlanInfo( - title: plan.plan.name, - description: plan.plan.details, - imagePath: plan.plan.image, - ), + PlanInfo(title: plan.plan.name, description: plan.plan.details, imagePath: plan.plan.image), const SizedBox(height: 8), - OptionWidget( - option: chosenOption, - disableDuration: true, - ), + OptionWidget(option: chosenOption, disableDuration: true), const SizedBox(height: 8), - PlanState( - state: plan.chosenPlanState, - ), + PlanState(state: plan.chosenPlanState), const SizedBox(height: 20), if (widget.successfulPayment == null) CustomOutlinedButton( @@ -184,13 +145,10 @@ class _InfoByOrderScreenContentState extends State { onPressed: () async { showModalBottomSheet( context: context, - builder: (context) => CancelPlanBottomSheet( - enableForm: plan.chosenPlanState.isStateAfterPaidState(), - planId: plan.id, - ), + builder: (context) => CancelPlanBottomSheet(enableForm: plan.chosenPlanState.isStateAfterPaidState(), planId: plan.id), ); }, - ) + ), ], ), ), diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index dc9e4c16..7baeee1a 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -249,6 +249,7 @@ class MessageLookup extends MessageLookupByLibrary { "cunsutation_was_paid_successfully": MessageLookupByLibrary.simpleMessage( "Поздравляем, консультация успешно оплачена", ), + "customer": MessageLookupByLibrary.simpleMessage("Покупатель"), "customer_price": MessageLookupByLibrary.simpleMessage( "Цена для покупателя", ), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 1055e664..ea858556 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -28,10 +28,9 @@ class S { static const AppLocalizationDelegate delegate = AppLocalizationDelegate(); static Future load(Locale locale) { - final name = - (locale.countryCode?.isEmpty ?? false) - ? locale.languageCode - : locale.toString(); + final name = (locale.countryCode?.isEmpty ?? false) + ? locale.languageCode + : locale.toString(); final localeName = Intl.canonicalizedLocale(name); return initializeMessages(localeName).then((_) { Intl.defaultLocale = localeName; @@ -3499,6 +3498,11 @@ class S { args: [], ); } + + /// `Покупатель` + String get customer { + return Intl.message('Покупатель', name: 'customer', desc: '', args: []); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 1fc575a8..5e7094b6 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -414,6 +414,7 @@ "delete": "Удалить", "cannotDeleteAccount": "Не получилось удалить ваш аккаунт", "resend": "Отправить повторно", - "notifications": "Уведомления" + "notifications": "Уведомления", + "customer": "Покупатель" }