From 9d1bedff26f2f1d5d6e86be9bde08a1674aaa49b Mon Sep 17 00:00:00 2001 From: misha Date: Wed, 11 Mar 2026 11:55:10 +0300 Subject: [PATCH] feat(diary): fixed consultant portion comments and day finishing errors handling --- lib/core/app_router.dart | 2 +- lib/core/routing/auth_routes.dart | 5 +--- .../diary/presentation/bloc/diary_cubit.dart | 16 +++++++++---- .../presentation/pages/diary_screen.dart | 10 +++++--- .../comment_for_consultant_widget.dart | 23 +++++++++++++++++-- .../widgets/finish_day_button_widget.dart | 10 ++++++++ .../widgets/portion_comment_widget.dart | 17 ++++++++++---- 7 files changed, 64 insertions(+), 19 deletions(-) diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index c98f7af2..4b778cbd 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -15,7 +15,7 @@ class AppRouter { static GlobalKey navigatorKey = GlobalKey(); // Домены для deep links (в т.ч. ссылки из пушей dev2.eatvio.com) - static const List _deepLinkHosts = ['eatvio.ru', 'www.eatvio.ru', 'dev2.eatvio.com', 'www.dev2.eatvio.com']; + static const List _deepLinkHosts = ['eatvio.ru', 'www.eatvio.ru']; static GoRouter appRouter = GoRouter( initialLocation: RouteConstants.updateScreen, diff --git a/lib/core/routing/auth_routes.dart b/lib/core/routing/auth_routes.dart index 772cd2eb..4d46e8de 100644 --- a/lib/core/routing/auth_routes.dart +++ b/lib/core/routing/auth_routes.dart @@ -10,17 +10,14 @@ import 'package:eatvio_app/features/registration/presentation/pages/verify_email import 'package:eatvio_app/features/registration_v2/presentation/pages/registration_v2_page.dart'; import 'package:go_router/go_router.dart'; -/// Маршруты для аутентификации class AuthRoutes { - /// Создание маршрутов аутентификации + static List createRoutes() { return [ GoRoute( path: RouteConstants.authScreen, name: 'authScreen', builder: (context, state) => const AuthScreen(), - // registerAnonymousPushToken вызывается в AuthCubit.initializeUser() - // и не должен блокировать навигацию routes: [ GoRoute(path: RouteConstants.registrationScreen, name: 'registrationScreen', builder: (context, state) => const RegistrationScreen()), GoRoute(path: RouteConstants.registrationScreenV2, name: 'registrationScreenV2', builder: (context, state) => const RegistrationV2Page()), diff --git a/lib/features/diary/presentation/bloc/diary_cubit.dart b/lib/features/diary/presentation/bloc/diary_cubit.dart index 93e24045..39a0a7e7 100644 --- a/lib/features/diary/presentation/bloc/diary_cubit.dart +++ b/lib/features/diary/presentation/bloc/diary_cubit.dart @@ -47,6 +47,7 @@ class DiaryCubit extends Cubit { } String comment = ''; + bool commentNeedsToBeFilled = false; void setInitialDate(DateTime? date) { if (date == null) return; @@ -68,7 +69,7 @@ class DiaryCubit extends Cubit { return canSelect; } - Future fetchDiaryV2({bool needLoading = false, int? diaryId, DateTime? date}) async { + Future fetchDiaryV2({bool needLoading = false, int? diaryId}) async { final currentState = state; try { if (needLoading) { @@ -283,9 +284,15 @@ class DiaryCubit extends Cubit { } } - Future finishDiaryDay({required int diaryId, required String wakeUp, required String retire}) async { - await diaryRepository.finishDiaryDay(diaryId: diaryId, wakeUp: wakeUp, retire: retire, comment: comment.isEmpty ? null : comment); - await updateCurrentDiary(); + Future finishDiaryDay({required int diaryId, required String wakeUp, required String retire}) async { + try { + await diaryRepository.finishDiaryDay(diaryId: diaryId, wakeUp: wakeUp, retire: retire, comment: comment.isEmpty ? null : comment); + await updateCurrentDiary(); + return true; + } catch (e, st) { + log(e.toString(), stackTrace: st); + return false; + } } Future getNutrition(int index) async { @@ -422,6 +429,7 @@ class DiaryCubit extends Cubit { void fillComment(String comment) { this.comment = comment; + commentNeedsToBeFilled = comment.isNotEmpty && comment.length < 3; } } diff --git a/lib/features/diary/presentation/pages/diary_screen.dart b/lib/features/diary/presentation/pages/diary_screen.dart index 07042f16..01c75db2 100644 --- a/lib/features/diary/presentation/pages/diary_screen.dart +++ b/lib/features/diary/presentation/pages/diary_screen.dart @@ -7,6 +7,7 @@ import 'package:eatvio_app/core/widgets/custom_outlined_button.dart'; import 'package:eatvio_app/core/widgets/data_error_widget.dart'; import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/core/widgets/main_background_bouble.dart'; +import 'package:eatvio_app/core/widgets/show_error_dialog.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_cubit.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart'; @@ -39,12 +40,12 @@ class UserDiaryScreen extends StatelessWidget { final int diaryId; @override Widget build(BuildContext context) { - return DiaryScreenContent(diaryId: diaryId,); + return DiaryScreenContent(diaryId: diaryId); } } class DiaryScreenContent extends StatefulWidget { - const DiaryScreenContent({super.key, this.diaryId,this.date}); + const DiaryScreenContent({super.key, this.diaryId, this.date}); final int? diaryId; final DateTime? date; @override @@ -134,11 +135,14 @@ class _DiaryScreenContentState extends State { setState(() { _isDiaryFinishing = true; }); - await context.read().finishDiaryDay( + final isFinished = await context.read().finishDiaryDay( diaryId: state.diary.id, wakeUp: state.diary.wakeup.toString(), retire: state.diary.retire.toString(), ); + if (!isFinished && context.mounted) { + showErrorDialog(context); + } setState(() { _isDiaryFinishing = false; }); diff --git a/lib/features/diary/presentation/widgets/comment_for_consultant_widget.dart b/lib/features/diary/presentation/widgets/comment_for_consultant_widget.dart index 3e311b59..bf73970a 100644 --- a/lib/features/diary/presentation/widgets/comment_for_consultant_widget.dart +++ b/lib/features/diary/presentation/widgets/comment_for_consultant_widget.dart @@ -2,6 +2,7 @@ import 'package:easy_debounce/easy_debounce.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_text_form_field.dart'; +import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -15,10 +16,14 @@ class CommentForConsultantWidget extends StatefulWidget { class _CommentForConsultantWidgetState extends State { final controller = TextEditingController(); + bool _commentNeedsToBeFilled = false; + @override void initState() { super.initState(); - if (widget.comment != null) controller.text = widget.comment ?? ''; + if (widget.comment != null) { + controller.text = widget.comment ?? ''; + } } @override @@ -41,13 +46,27 @@ class _CommentForConsultantWidgetState extends State CustomTextFormField( controller: controller, onChanged: (value) { - EasyDebounce.debounce('client-typing', const Duration(milliseconds: 500), () => context.read().fillComment(value)); + EasyDebounce.debounce( + 'client-typing', + const Duration(milliseconds: 500), + () => context.read().fillComment(value), + ); + if (widget.comment == null) { + setState(() { + _commentNeedsToBeFilled = value.isNotEmpty && value.length < 3; + }); + } }, + hasError: _commentNeedsToBeFilled, hintText: 'Укажите дополнительную информацию', minLines: 4, maxLines: 4, readOnly: widget.comment != null, ), + if (_commentNeedsToBeFilled) ...[ + const SizedBox(height: 4), + const ValidationErrorWidget(error: 'Минимум 3 символа'), + ], ], ), ), diff --git a/lib/features/diary/presentation/widgets/finish_day_button_widget.dart b/lib/features/diary/presentation/widgets/finish_day_button_widget.dart index ba13dd96..d38e32e3 100644 --- a/lib/features/diary/presentation/widgets/finish_day_button_widget.dart +++ b/lib/features/diary/presentation/widgets/finish_day_button_widget.dart @@ -4,7 +4,9 @@ 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'; import 'package:eatvio_app/core/widgets/show_error_dialog.dart'; +import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; class FinishDayButtonWidget extends StatefulWidget { final Future Function() onPressed; @@ -18,6 +20,14 @@ class FinishDayButtonWidget extends StatefulWidget { class _FinishDayButtonWidgetState extends State { void _onTap() { + if (context.read().commentNeedsToBeFilled) { + showErrorDialog( + context, + title: 'Комментарий слишком короткий', + description: 'Минимум 3 символа в комментарии для консультанта.', + ); + return; + } if (!widget.hasProducts) { showErrorDialog( context, diff --git a/lib/features/diary/presentation/widgets/portion_comment_widget.dart b/lib/features/diary/presentation/widgets/portion_comment_widget.dart index f1acbdc1..8c4c1b13 100644 --- a/lib/features/diary/presentation/widgets/portion_comment_widget.dart +++ b/lib/features/diary/presentation/widgets/portion_comment_widget.dart @@ -22,6 +22,11 @@ class PortionCommentWidget extends StatefulWidget { class _PortionCommentWidgetState extends State { final controller = TextEditingController(); @override + void dispose() { + controller.dispose(); + super.dispose(); + } + @override void didUpdateWidget(PortionCommentWidget oldWidget) { super.didUpdateWidget(oldWidget); if (widget.portionId != oldWidget.portionId) { @@ -57,17 +62,19 @@ class _PortionCommentWidgetState extends State { hintText: s.typePortionComment, hasError: hasError, onChanged: (value) { + final cubit = context.read(); EasyDebounce.debounce( - 'client-typing', + 'portion-comment-${widget.portionId}', const Duration(milliseconds: 500), () { - final hasPortionComment = state.commentsModel.portions.any((portion) => portion.id == widget.portionId); + final currentState = cubit.state; + final hasPortionComment = currentState.commentsModel.portions.any((portion) => portion.id == widget.portionId); if (hasPortionComment) { - final portion = state.commentsModel.portions.firstWhere((portion) => portion.id == widget.portionId); - context.read().updatePortionComment(portion.copyWith(comment: value)); + final portion = currentState.commentsModel.portions.firstWhere((portion) => portion.id == widget.portionId); + cubit.updatePortionComment(portion.copyWith(comment: value)); return; } - context.read().addPortionComment(PortionCommentModel(id: widget.portionId, comment: value)); + cubit.addPortionComment(PortionCommentModel(id: widget.portionId, comment: value)); }, ); },