diff --git a/lib/features/personal_diet_suggestion/presentation/consultation_widget.dart b/lib/features/personal_diet_suggestion/presentation/consultation_widget.dart index a6846e57..9244f588 100644 --- a/lib/features/personal_diet_suggestion/presentation/consultation_widget.dart +++ b/lib/features/personal_diet_suggestion/presentation/consultation_widget.dart @@ -5,6 +5,7 @@ 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/localization_utils.dart'; +import 'package:eatvio_app/core/utils/toasts.dart'; import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart'; import 'package:eatvio_app/core/widgets/bottom_sheet_with_blur.dart'; import 'package:eatvio_app/core/widgets/custom_outlined_button.dart'; @@ -181,35 +182,8 @@ class ConsultationWidget extends StatelessWidget { userName: consultationModel.userName, dietName: dietName, onTap: () async { - final result = await context - .read() - .reattachDietToConsultation( - consultationId: consultationModel.id, - ); AppRouter.navigatorKey.currentContext?.pop(); - if (result) { - if (!context.mounted) return; - showBottomSheetWithBlur( - userRootNavigator: true, - context: context, - height: null, - child: SuggestionSuccessBottomSheet( - userName: consultationModel.userName, - dietName: dietName, - ), - ); - return; - } - if (!context.mounted) return; - showBottomSheetWithBlur( - userRootNavigator: true, - context: context, - height: null, - child: SuggestionFailureBottomSheet( - userName: consultationModel.userName, - dietName: dietName, - ), - ); + await _attachDiet(context, reattach: true); }, ), ); @@ -241,34 +215,7 @@ class ConsultationWidget extends StatelessWidget { dietName: dietName, onTap: () async { AppRouter.navigatorKey.currentContext?.pop(); - final result = await context - .read() - .attachDietToConsultation( - consultationId: consultationModel.id, - ); - if (result) { - if (!context.mounted) return; - showBottomSheetWithBlur( - userRootNavigator: true, - context: context, - height: null, - child: SuggestionSuccessBottomSheet( - userName: consultationModel.userName, - dietName: dietName, - ), - ); - return; - } - if (!context.mounted) return; - showBottomSheetWithBlur( - userRootNavigator: true, - context: context, - height: null, - child: SuggestionFailureBottomSheet( - userName: consultationModel.userName, - dietName: dietName, - ), - ); + await _attachDiet(context, reattach: false); }, ), ); @@ -278,6 +225,41 @@ class ConsultationWidget extends StatelessWidget { ), ); } + + Future _attachDiet( + BuildContext context, { + required bool reattach, + }) async { + final router = GoRouter.of(context); + final chatId = consultationModel.chatId; + final noChatMessage = S.of(context).msg_something_went_wrong; + final cubit = context.read(); + final result = reattach + ? await cubit.reattachDietToConsultation( + consultationId: consultationModel.id, + ) + : await cubit.attachDietToConsultation( + consultationId: consultationModel.id, + ); + if (!result) { + if (!context.mounted) return; + showBottomSheetWithBlur( + userRootNavigator: true, + context: context, + height: null, + child: SuggestionFailureBottomSheet( + userName: consultationModel.userName, + dietName: dietName, + ), + ); + return; + } + if (chatId == null || chatId.isEmpty) { + showErrorToast(noChatMessage); + return; + } + router.goNamed('chatScreen', queryParameters: {'uuid': chatId}); + } } class SuggestDietBottomSheet extends StatefulWidget { @@ -336,65 +318,6 @@ class _SuggestDietBottomSheet extends State { } } -class SuggestionSuccessBottomSheet extends StatefulWidget { - const SuggestionSuccessBottomSheet({ - super.key, - required this.userName, - required this.dietName, - }); - final String userName; - final String dietName; - - @override - State createState() => - _SuggestionSuccessBottomSheet(); -} - -class _SuggestionSuccessBottomSheet - extends State { - @override - Widget build(BuildContext context) { - final s = S.of(context); - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 12.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(height: 8), - const BottomSheetHandlerBar(), - const SizedBox(height: 20), - Text( - 'Предложение диеты', - style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), - ), - const SizedBox(height: 16), - Text( - '«${widget.dietName}» предложена пользователю ${widget.userName}', - style: TextStyles.textSMedium.copyWith(color: appTheme.black), - textAlign: TextAlign.center, - ), - const SizedBox(height: 8), - CustomOutlinedButton( - text: s.ok, - height: 52, - buttonTextStyle: TextStyles.textMSemibold.copyWith( - color: appTheme.whiteA700, - ), - buttonStyle: ButtonStyle( - side: const WidgetStatePropertyAll(BorderSide.none), - backgroundColor: WidgetStatePropertyAll(appTheme.blueA400), - ), - onPressed: () async { - AppRouter.navigatorKey.currentContext?.pop(); - }, - ), - const SizedBox(height: 40), - ], - ), - ); - } -} - class SuggestionFailureBottomSheet extends StatefulWidget { const SuggestionFailureBottomSheet({ super.key, diff --git a/test/features/personal_diet_suggestion/presentation/consultation_widget_test.dart b/test/features/personal_diet_suggestion/presentation/consultation_widget_test.dart index 6aef200f..11b8d568 100644 --- a/test/features/personal_diet_suggestion/presentation/consultation_widget_test.dart +++ b/test/features/personal_diet_suggestion/presentation/consultation_widget_test.dart @@ -35,6 +35,7 @@ void main() { final mockConsultation = ConsultationModel( id: 1, + chatId: 'chat-1', chosenPlanState: ChosenPlanStateEnum.stateInProgress, planTitle: 'Test Plan', userName: 'Test User', @@ -69,6 +70,32 @@ void main() { path: '/personal-diet-suggestion', builder: (context, state) => const Scaffold(body: Text('Personal Diet')), ), + GoRoute( + path: '/chat-select-screen', + name: 'chatSelectScreen', + builder: (context, state) => const Scaffold(body: Text('Chats')), + ), + GoRoute( + path: '/chat-screen', + name: 'chatScreen', + builder: (context, state) { + final uuid = state.uri.queryParameters['uuid']; + return Scaffold( + appBar: AppBar( + leading: BackButton( + onPressed: () { + if (context.canPop()) { + GoRouter.of(context).pop(); + return; + } + GoRouter.of(context).go('/chat-select-screen'); + }, + ), + ), + body: Text('Chat $uuid'), + ); + }, + ), ], ), ); @@ -202,7 +229,7 @@ void main() { }); }); - testWidgets('shows success bottom sheet after successful diet attachment', (tester) async { + testWidgets('opens chat after successful diet attachment', (tester) async { await mockNetworkImagesFor(() async { final cubit = MockPersonalDietSuggestionCubit(); @@ -227,16 +254,22 @@ void main() { await tester.tap(offerButton); await tester.pumpAndSettle(); - expect(find.text('«Test Diet» предложена пользователю Test User'), findsOneWidget); + expect(find.text('Chat chat-1'), findsOneWidget); verify(cubit.attachDietToConsultation(consultationId: 1)).called(1); + + await tester.tap(find.byType(BackButton)); + await tester.pumpAndSettle(); + + expect(find.text('Chats'), findsOneWidget); }); }); - testWidgets('shows success bottom sheet after successful diet reattachment', (tester) async { + testWidgets('opens chat after successful diet reattachment', (tester) async { await mockNetworkImagesFor(() async { final cubit = MockPersonalDietSuggestionCubit(); final consultationWithDiet = ConsultationModel( id: 1, + chatId: 'chat-2', chosenPlanState: ChosenPlanStateEnum.stateInProgress, planTitle: 'Test Plan', userName: 'Test User', @@ -274,9 +307,47 @@ void main() { await tester.tap(offerButton); await tester.pumpAndSettle(); - expect(find.text('«Test Diet» предложена пользователю Test User'), findsOneWidget); + expect(find.text('Chat chat-2'), findsOneWidget); verify(cubit.reattachDietToConsultation(consultationId: 1)).called(1); verifyNever(cubit.attachDietToConsultation(consultationId: 1)); + + await tester.tap(find.byType(BackButton)); + await tester.pumpAndSettle(); + + expect(find.text('Chats'), findsOneWidget); + }); + }); + + testWidgets('stays on screen when chat id is missing after success', (tester) async { + await mockNetworkImagesFor(() async { + final cubit = MockPersonalDietSuggestionCubit(); + + when(cubit.state).thenReturn(PersonalDietSuggestionState()); + when(cubit.stream).thenAnswer((_) => const Stream.empty()); + when(cubit.close()).thenAnswer((_) async => Future.value()); + when(cubit.attachDietToConsultation(consultationId: 1)).thenAnswer((_) async => true); + + await tester.pumpWidget( + BlocProvider.value( + value: cubit, + child: wrap( + ConsultationWidget( + consultationModel: mockConsultation.copyWith(chatId: null), + dietName: 'Test Diet', + ), + ), + ), + ); + await tester.pumpAndSettle(); + + await tester.tap(find.text('Предложить диету')); + await tester.pumpAndSettle(); + await tester.tap(find.text(S.of(tester.element(find.byType(ConsultationWidget))).toOffer)); + await tester.pumpAndSettle(); + + expect(find.textContaining('Chat '), findsNothing); + expect(find.text('Test User'), findsOneWidget); + verify(cubit.attachDietToConsultation(consultationId: 1)).called(1); }); });