From 1178525a93c0ad4e7cc0165ffd477b7232b359fa Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Fri, 25 Apr 2025 11:03:52 +0300 Subject: [PATCH 01/26] =?UTF-8?q?fix(feed):=20=D0=BF=D0=BE=D0=BA=D0=B0?= =?UTF-8?q?=D0=B7=D0=B0=D1=82=D1=8C=20=D0=B5=D1=89=D1=91=20(338),=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=80=D0=B8=D0=B8=20?= =?UTF-8?q?(325),=20=D0=BF=D0=B5=D1=80=D0=B5=D1=85=D0=BE=D0=B4=20=D0=B2=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D1=84=D0=B8=D0=BB=D1=8C=20(317)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/const.dart | 4 +- .../presentation/widgets/author_card.dart | 127 +++++++++--------- .../presentation/widgets/comments_widget.dart | 1 + .../presentation/widgets/diets_widget.dart | 13 ++ .../presentation/widgets/plan_widget.dart | 13 ++ .../presentation/widgets/recipes_widget.dart | 2 +- 6 files changed, 95 insertions(+), 65 deletions(-) diff --git a/lib/core/const.dart b/lib/core/const.dart index 4f38a54e..31c47e9c 100644 --- a/lib/core/const.dart +++ b/lib/core/const.dart @@ -1,5 +1,5 @@ -// String baseUrl = 'https://eatvio.ru'; -String baseUrl = 'https://dev2.eatvio.com'; +String baseUrl = 'https://eatvio.ru'; +// String baseUrl = 'https://dev2.eatvio.com'; const List durationValues = [7, 14, 21]; diff --git a/lib/features/diets/presentation/widgets/author_card.dart b/lib/features/diets/presentation/widgets/author_card.dart index 9e00d1dd..c1113f93 100644 --- a/lib/features/diets/presentation/widgets/author_card.dart +++ b/lib/features/diets/presentation/widgets/author_card.dart @@ -12,75 +12,78 @@ class AuthorCard extends StatelessWidget { final String title; final Color? backgroundColor; final TextStyle? titleStyle; + @override Widget build(BuildContext context) { - return Container( - width: MediaQuery.sizeOf(context).width, - decoration: BoxDecoration(borderRadius: BorderRadius.circular(12), color: backgroundColor ?? appTheme.cardBackground), - child: Stack( - children: [ - Padding( - padding: const EdgeInsets.only(left: 16, top: 16, bottom: 24), - child: Column( - children: [ - ListTile( - contentPadding: EdgeInsets.zero, - leading: Container( - width: 64, - height: 64, - decoration: BoxDecoration( - shape: BoxShape.circle, - image: DecorationImage( - image: NetworkImage(author.authorProfileIcon), - fit: BoxFit.cover, + return GestureDetector( + onTap: () { + GoRouter.of(context).pushNamed('profileScreen', extra: author); + }, + child: Container( + width: MediaQuery.sizeOf(context).width, + decoration: BoxDecoration(borderRadius: BorderRadius.circular(12), color: backgroundColor ?? appTheme.cardBackground), + child: Stack( + children: [ + Padding( + padding: const EdgeInsets.only(left: 16, top: 16, bottom: 24), + child: Column( + children: [ + ListTile( + contentPadding: EdgeInsets.zero, + leading: Container( + width: 64, + height: 64, + decoration: BoxDecoration( + shape: BoxShape.circle, + image: DecorationImage( + image: NetworkImage(author.authorProfileIcon), + fit: BoxFit.cover, + ), ), ), - ), - subtitle: Text( - 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( + subtitle: Text( + author.authorName, + style: TextStyles.textLSemibold.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, - ), - ), - ], + 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, + ), + ), + ], + ), + ], + ), + ], + ), ), - ), - Positioned( - right: 8, - top: 8, - child: GestureDetector( - onTap: () => GoRouter.of(context).pushNamed('profileScreen', extra: author), + Positioned( + right: 8, + top: 8, child: Container( width: 28, height: 28, @@ -98,8 +101,8 @@ class AuthorCard extends StatelessWidget { ), ), ), - ), - ], + ], + ), ), ); } diff --git a/lib/features/feed/presentation/widgets/comments_widget.dart b/lib/features/feed/presentation/widgets/comments_widget.dart index 109bfb91..784a77c5 100644 --- a/lib/features/feed/presentation/widgets/comments_widget.dart +++ b/lib/features/feed/presentation/widgets/comments_widget.dart @@ -42,6 +42,7 @@ class _CommentsWidgetState extends State { ), const SizedBox(height: 8), ], + const SizedBox(height: 48), ], ); } diff --git a/lib/features/feed/presentation/widgets/diets_widget.dart b/lib/features/feed/presentation/widgets/diets_widget.dart index ef406b3b..fa9dce69 100644 --- a/lib/features/feed/presentation/widgets/diets_widget.dart +++ b/lib/features/feed/presentation/widgets/diets_widget.dart @@ -153,6 +153,19 @@ class _DietsWidgetState extends State { ), ), ], + if (widget.diet.details.length > 200) + const Padding( + padding: EdgeInsets.only(left: 16, top: 8), + child: Text( + 'Показать ещё', + style: TextStyle( + height: 1, + color: Color(0xFF8B8B8B), + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ), ], ), Padding( diff --git a/lib/features/feed/presentation/widgets/plan_widget.dart b/lib/features/feed/presentation/widgets/plan_widget.dart index 56fcd4e5..2951d90a 100644 --- a/lib/features/feed/presentation/widgets/plan_widget.dart +++ b/lib/features/feed/presentation/widgets/plan_widget.dart @@ -156,6 +156,19 @@ class _PlanWidgetState extends State { }, ), ), + if (widget.plan.details.length > 200) + const Padding( + padding: EdgeInsets.only(left: 16, top: 8), + child: Text( + 'Показать ещё', + style: TextStyle( + height: 1, + color: Color(0xFF8B8B8B), + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ), ], ], ), diff --git a/lib/features/feed/presentation/widgets/recipes_widget.dart b/lib/features/feed/presentation/widgets/recipes_widget.dart index 8d51da7c..2a586190 100644 --- a/lib/features/feed/presentation/widgets/recipes_widget.dart +++ b/lib/features/feed/presentation/widgets/recipes_widget.dart @@ -187,7 +187,7 @@ class _RecipesWidgetState extends State { ), if (widget.recipe.description.length > 200) const Padding( - padding: EdgeInsets.symmetric(horizontal: 12), + padding: EdgeInsets.only(left: 16, top: 8), child: Text( 'Показать ещё', style: TextStyle( From a3d903b6b964c35e6545f771ed14e411c512633b Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Fri, 25 Apr 2025 11:04:18 +0300 Subject: [PATCH 02/26] remove ignored files --- .../cubit/cancel_plan_state.freezed.dart | 82 -- .../models/cancelation_model.freezed.dart | 184 ---- .../models/cancelation_model.g.dart | 21 - .../service/cancel_plan_service.g.dart | 69 -- .../models/consultation_model.freezed.dart | 799 ------------------ .../models/consultation_model.g.dart | 100 --- .../cubit/consultations_state.freezed.dart | 175 ---- .../services/consultation_service.g.dart | 73 -- .../consultant_comment_model.freezed.dart | 581 ------------- .../models/consultant_comment_model.g.dart | 59 -- .../consultant_review_model.freezed.dart | 796 ----------------- .../models/consultant_review_model.g.dart | 69 -- .../consultant_comments_state.freezed.dart | 190 ----- .../models/notification_model.freezed.dart | 166 ---- .../models/notification_model.g.dart | 28 - 15 files changed, 3392 deletions(-) delete mode 100644 lib/features/cancel_plan_bottom_sheet/cubit/cancel_plan_state.freezed.dart delete mode 100644 lib/features/cancel_plan_bottom_sheet/models/cancelation_model.freezed.dart delete mode 100644 lib/features/cancel_plan_bottom_sheet/models/cancelation_model.g.dart delete mode 100644 lib/features/cancel_plan_bottom_sheet/service/cancel_plan_service.g.dart delete mode 100644 lib/features/consultations/models/consultation_model.freezed.dart delete mode 100644 lib/features/consultations/models/consultation_model.g.dart delete mode 100644 lib/features/consultations/presentation/cubit/consultations_state.freezed.dart delete mode 100644 lib/features/consultations/services/consultation_service.g.dart delete mode 100644 lib/features/diary/models/consultant_comment_model.freezed.dart delete mode 100644 lib/features/diary/models/consultant_comment_model.g.dart delete mode 100644 lib/features/diary/models/consultant_review_model.freezed.dart delete mode 100644 lib/features/diary/models/consultant_review_model.g.dart delete mode 100644 lib/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_state.freezed.dart delete mode 100644 lib/features/notifications/models/notification_model.freezed.dart delete mode 100644 lib/features/notifications/models/notification_model.g.dart diff --git a/lib/features/cancel_plan_bottom_sheet/cubit/cancel_plan_state.freezed.dart b/lib/features/cancel_plan_bottom_sheet/cubit/cancel_plan_state.freezed.dart deleted file mode 100644 index 90f3350c..00000000 --- a/lib/features/cancel_plan_bottom_sheet/cubit/cancel_plan_state.freezed.dart +++ /dev/null @@ -1,82 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'cancel_plan_state.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -/// @nodoc -mixin _$CancelPlanState {} - -/// @nodoc -abstract class $CancelPlanStateCopyWith<$Res> { - factory $CancelPlanStateCopyWith( - CancelPlanState value, $Res Function(CancelPlanState) then) = - _$CancelPlanStateCopyWithImpl<$Res, CancelPlanState>; -} - -/// @nodoc -class _$CancelPlanStateCopyWithImpl<$Res, $Val extends CancelPlanState> - implements $CancelPlanStateCopyWith<$Res> { - _$CancelPlanStateCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of CancelPlanState - /// with the given fields replaced by the non-null parameter values. -} - -/// @nodoc -abstract class _$$CancelPlanStateImplCopyWith<$Res> { - factory _$$CancelPlanStateImplCopyWith(_$CancelPlanStateImpl value, - $Res Function(_$CancelPlanStateImpl) then) = - __$$CancelPlanStateImplCopyWithImpl<$Res>; -} - -/// @nodoc -class __$$CancelPlanStateImplCopyWithImpl<$Res> - extends _$CancelPlanStateCopyWithImpl<$Res, _$CancelPlanStateImpl> - implements _$$CancelPlanStateImplCopyWith<$Res> { - __$$CancelPlanStateImplCopyWithImpl( - _$CancelPlanStateImpl _value, $Res Function(_$CancelPlanStateImpl) _then) - : super(_value, _then); - - /// Create a copy of CancelPlanState - /// with the given fields replaced by the non-null parameter values. -} - -/// @nodoc - -class _$CancelPlanStateImpl implements _CancelPlanState { - _$CancelPlanStateImpl(); - - @override - String toString() { - return 'CancelPlanState()'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && other is _$CancelPlanStateImpl); - } - - @override - int get hashCode => runtimeType.hashCode; -} - -abstract class _CancelPlanState implements CancelPlanState { - factory _CancelPlanState() = _$CancelPlanStateImpl; -} diff --git a/lib/features/cancel_plan_bottom_sheet/models/cancelation_model.freezed.dart b/lib/features/cancel_plan_bottom_sheet/models/cancelation_model.freezed.dart deleted file mode 100644 index d68a42ba..00000000 --- a/lib/features/cancel_plan_bottom_sheet/models/cancelation_model.freezed.dart +++ /dev/null @@ -1,184 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'cancelation_model.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -CancelationModel _$CancelationModelFromJson(Map json) { - return _CancelationModel.fromJson(json); -} - -/// @nodoc -mixin _$CancelationModel { - String get description => throw _privateConstructorUsedError; - int get price => throw _privateConstructorUsedError; - - /// Serializes this CancelationModel to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of CancelationModel - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $CancelationModelCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $CancelationModelCopyWith<$Res> { - factory $CancelationModelCopyWith( - CancelationModel value, $Res Function(CancelationModel) then) = - _$CancelationModelCopyWithImpl<$Res, CancelationModel>; - @useResult - $Res call({String description, int price}); -} - -/// @nodoc -class _$CancelationModelCopyWithImpl<$Res, $Val extends CancelationModel> - implements $CancelationModelCopyWith<$Res> { - _$CancelationModelCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of CancelationModel - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? description = null, - Object? price = null, - }) { - return _then(_value.copyWith( - description: null == description - ? _value.description - : description // ignore: cast_nullable_to_non_nullable - as String, - price: null == price - ? _value.price - : price // ignore: cast_nullable_to_non_nullable - as int, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$CancelationModelImplCopyWith<$Res> - implements $CancelationModelCopyWith<$Res> { - factory _$$CancelationModelImplCopyWith(_$CancelationModelImpl value, - $Res Function(_$CancelationModelImpl) then) = - __$$CancelationModelImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({String description, int price}); -} - -/// @nodoc -class __$$CancelationModelImplCopyWithImpl<$Res> - extends _$CancelationModelCopyWithImpl<$Res, _$CancelationModelImpl> - implements _$$CancelationModelImplCopyWith<$Res> { - __$$CancelationModelImplCopyWithImpl(_$CancelationModelImpl _value, - $Res Function(_$CancelationModelImpl) _then) - : super(_value, _then); - - /// Create a copy of CancelationModel - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? description = null, - Object? price = null, - }) { - return _then(_$CancelationModelImpl( - description: null == description - ? _value.description - : description // ignore: cast_nullable_to_non_nullable - as String, - price: null == price - ? _value.price - : price // ignore: cast_nullable_to_non_nullable - as int, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$CancelationModelImpl implements _CancelationModel { - _$CancelationModelImpl({required this.description, required this.price}); - - factory _$CancelationModelImpl.fromJson(Map json) => - _$$CancelationModelImplFromJson(json); - - @override - final String description; - @override - final int price; - - @override - String toString() { - return 'CancelationModel(description: $description, price: $price)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$CancelationModelImpl && - (identical(other.description, description) || - other.description == description) && - (identical(other.price, price) || other.price == price)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, description, price); - - /// Create a copy of CancelationModel - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$CancelationModelImplCopyWith<_$CancelationModelImpl> get copyWith => - __$$CancelationModelImplCopyWithImpl<_$CancelationModelImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$CancelationModelImplToJson( - this, - ); - } -} - -abstract class _CancelationModel implements CancelationModel { - factory _CancelationModel( - {required final String description, - required final int price}) = _$CancelationModelImpl; - - factory _CancelationModel.fromJson(Map json) = - _$CancelationModelImpl.fromJson; - - @override - String get description; - @override - int get price; - - /// Create a copy of CancelationModel - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$CancelationModelImplCopyWith<_$CancelationModelImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/features/cancel_plan_bottom_sheet/models/cancelation_model.g.dart b/lib/features/cancel_plan_bottom_sheet/models/cancelation_model.g.dart deleted file mode 100644 index d7bd21e9..00000000 --- a/lib/features/cancel_plan_bottom_sheet/models/cancelation_model.g.dart +++ /dev/null @@ -1,21 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'cancelation_model.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$CancelationModelImpl _$$CancelationModelImplFromJson( - Map json) => - _$CancelationModelImpl( - description: json['description'] as String, - price: (json['price'] as num).toInt(), - ); - -Map _$$CancelationModelImplToJson( - _$CancelationModelImpl instance) => - { - 'description': instance.description, - 'price': instance.price, - }; diff --git a/lib/features/cancel_plan_bottom_sheet/service/cancel_plan_service.g.dart b/lib/features/cancel_plan_bottom_sheet/service/cancel_plan_service.g.dart deleted file mode 100644 index 943e9d25..00000000 --- a/lib/features/cancel_plan_bottom_sheet/service/cancel_plan_service.g.dart +++ /dev/null @@ -1,69 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'cancel_plan_service.dart'; - -// ************************************************************************** -// RetrofitGenerator -// ************************************************************************** - -// ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers,unused_element,unnecessary_string_interpolations - -class _CancelPlanService implements CancelPlanService { - _CancelPlanService(this._dio, {this.baseUrl, this.errorLogger}); - - final Dio _dio; - - String? baseUrl; - - final ParseErrorLogger? errorLogger; - - @override - Future cancelChosenPlan({ - required int id, - CancelationModel? body, - }) async { - final _extra = {}; - final queryParameters = {}; - queryParameters.removeWhere((k, v) => v == null); - final _headers = {}; - final _data = body; - final _options = _setStreamType( - Options(method: 'POST', headers: _headers, extra: _extra) - .compose( - _dio.options, - '/api/chosen_plans/${id}/cancel', - queryParameters: queryParameters, - data: _data, - ) - .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), - ); - await _dio.fetch(_options); - } - - RequestOptions _setStreamType(RequestOptions requestOptions) { - if (T != dynamic && - !(requestOptions.responseType == ResponseType.bytes || - requestOptions.responseType == ResponseType.stream)) { - if (T == String) { - requestOptions.responseType = ResponseType.plain; - } else { - requestOptions.responseType = ResponseType.json; - } - } - return requestOptions; - } - - String _combineBaseUrls(String dioBaseUrl, String? baseUrl) { - if (baseUrl == null || baseUrl.trim().isEmpty) { - return dioBaseUrl; - } - - final url = Uri.parse(baseUrl); - - if (url.isAbsolute) { - return url.toString(); - } - - return Uri.parse(dioBaseUrl).resolveUri(url).toString(); - } -} diff --git a/lib/features/consultations/models/consultation_model.freezed.dart b/lib/features/consultations/models/consultation_model.freezed.dart deleted file mode 100644 index 0fba8726..00000000 --- a/lib/features/consultations/models/consultation_model.freezed.dart +++ /dev/null @@ -1,799 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'consultation_model.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -ConsultationModel _$ConsultationModelFromJson(Map json) { - return _ConsultationModel.fromJson(json); -} - -/// @nodoc -mixin _$ConsultationModel { - int get id => throw _privateConstructorUsedError; - @JsonKey(name: 'last_state') - ChosenPlanStateEnum get chosenPlanState => throw _privateConstructorUsedError; - String get planTitle => throw _privateConstructorUsedError; - String get userName => throw _privateConstructorUsedError; - String get userSlug => throw _privateConstructorUsedError; - double get consultantPayment => throw _privateConstructorUsedError; - int get duration => throw _privateConstructorUsedError; - int get currentDiaryDay => throw _privateConstructorUsedError; - @JsonKey(name: 'start_date', fromJson: parseDate) - DateTime get startDate => throw _privateConstructorUsedError; - @JsonKey(name: 'end_date', fromJson: parseDate) - DateTime get endDate => throw _privateConstructorUsedError; - @JsonKey(name: 'consultant_id') - int get consultantId => throw _privateConstructorUsedError; - List get diaries => throw _privateConstructorUsedError; - - /// Serializes this ConsultationModel to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of ConsultationModel - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $ConsultationModelCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $ConsultationModelCopyWith<$Res> { - factory $ConsultationModelCopyWith( - ConsultationModel value, $Res Function(ConsultationModel) then) = - _$ConsultationModelCopyWithImpl<$Res, ConsultationModel>; - @useResult - $Res call( - {int id, - @JsonKey(name: 'last_state') ChosenPlanStateEnum chosenPlanState, - String planTitle, - String userName, - String userSlug, - double consultantPayment, - int duration, - int currentDiaryDay, - @JsonKey(name: 'start_date', fromJson: parseDate) DateTime startDate, - @JsonKey(name: 'end_date', fromJson: parseDate) DateTime endDate, - @JsonKey(name: 'consultant_id') int consultantId, - List diaries}); -} - -/// @nodoc -class _$ConsultationModelCopyWithImpl<$Res, $Val extends ConsultationModel> - implements $ConsultationModelCopyWith<$Res> { - _$ConsultationModelCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of ConsultationModel - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? id = null, - Object? chosenPlanState = null, - Object? planTitle = null, - Object? userName = null, - Object? userSlug = null, - Object? consultantPayment = null, - Object? duration = null, - Object? currentDiaryDay = null, - Object? startDate = null, - Object? endDate = null, - Object? consultantId = null, - Object? diaries = null, - }) { - return _then(_value.copyWith( - id: null == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int, - chosenPlanState: null == chosenPlanState - ? _value.chosenPlanState - : chosenPlanState // ignore: cast_nullable_to_non_nullable - as ChosenPlanStateEnum, - planTitle: null == planTitle - ? _value.planTitle - : planTitle // ignore: cast_nullable_to_non_nullable - as String, - userName: null == userName - ? _value.userName - : userName // ignore: cast_nullable_to_non_nullable - as String, - userSlug: null == userSlug - ? _value.userSlug - : userSlug // ignore: cast_nullable_to_non_nullable - as String, - consultantPayment: null == consultantPayment - ? _value.consultantPayment - : consultantPayment // ignore: cast_nullable_to_non_nullable - as double, - duration: null == duration - ? _value.duration - : duration // ignore: cast_nullable_to_non_nullable - as int, - currentDiaryDay: null == currentDiaryDay - ? _value.currentDiaryDay - : currentDiaryDay // ignore: cast_nullable_to_non_nullable - as int, - startDate: null == startDate - ? _value.startDate - : startDate // ignore: cast_nullable_to_non_nullable - as DateTime, - endDate: null == endDate - ? _value.endDate - : endDate // ignore: cast_nullable_to_non_nullable - as DateTime, - consultantId: null == consultantId - ? _value.consultantId - : consultantId // ignore: cast_nullable_to_non_nullable - as int, - diaries: null == diaries - ? _value.diaries - : diaries // ignore: cast_nullable_to_non_nullable - as List, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$ConsultationModelImplCopyWith<$Res> - implements $ConsultationModelCopyWith<$Res> { - factory _$$ConsultationModelImplCopyWith(_$ConsultationModelImpl value, - $Res Function(_$ConsultationModelImpl) then) = - __$$ConsultationModelImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {int id, - @JsonKey(name: 'last_state') ChosenPlanStateEnum chosenPlanState, - String planTitle, - String userName, - String userSlug, - double consultantPayment, - int duration, - int currentDiaryDay, - @JsonKey(name: 'start_date', fromJson: parseDate) DateTime startDate, - @JsonKey(name: 'end_date', fromJson: parseDate) DateTime endDate, - @JsonKey(name: 'consultant_id') int consultantId, - List diaries}); -} - -/// @nodoc -class __$$ConsultationModelImplCopyWithImpl<$Res> - extends _$ConsultationModelCopyWithImpl<$Res, _$ConsultationModelImpl> - implements _$$ConsultationModelImplCopyWith<$Res> { - __$$ConsultationModelImplCopyWithImpl(_$ConsultationModelImpl _value, - $Res Function(_$ConsultationModelImpl) _then) - : super(_value, _then); - - /// Create a copy of ConsultationModel - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? id = null, - Object? chosenPlanState = null, - Object? planTitle = null, - Object? userName = null, - Object? userSlug = null, - Object? consultantPayment = null, - Object? duration = null, - Object? currentDiaryDay = null, - Object? startDate = null, - Object? endDate = null, - Object? consultantId = null, - Object? diaries = null, - }) { - return _then(_$ConsultationModelImpl( - id: null == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int, - chosenPlanState: null == chosenPlanState - ? _value.chosenPlanState - : chosenPlanState // ignore: cast_nullable_to_non_nullable - as ChosenPlanStateEnum, - planTitle: null == planTitle - ? _value.planTitle - : planTitle // ignore: cast_nullable_to_non_nullable - as String, - userName: null == userName - ? _value.userName - : userName // ignore: cast_nullable_to_non_nullable - as String, - userSlug: null == userSlug - ? _value.userSlug - : userSlug // ignore: cast_nullable_to_non_nullable - as String, - consultantPayment: null == consultantPayment - ? _value.consultantPayment - : consultantPayment // ignore: cast_nullable_to_non_nullable - as double, - duration: null == duration - ? _value.duration - : duration // ignore: cast_nullable_to_non_nullable - as int, - currentDiaryDay: null == currentDiaryDay - ? _value.currentDiaryDay - : currentDiaryDay // ignore: cast_nullable_to_non_nullable - as int, - startDate: null == startDate - ? _value.startDate - : startDate // ignore: cast_nullable_to_non_nullable - as DateTime, - endDate: null == endDate - ? _value.endDate - : endDate // ignore: cast_nullable_to_non_nullable - as DateTime, - consultantId: null == consultantId - ? _value.consultantId - : consultantId // ignore: cast_nullable_to_non_nullable - as int, - diaries: null == diaries - ? _value._diaries - : diaries // ignore: cast_nullable_to_non_nullable - as List, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$ConsultationModelImpl implements _ConsultationModel { - _$ConsultationModelImpl( - {required this.id, - @JsonKey(name: 'last_state') required this.chosenPlanState, - required this.planTitle, - required this.userName, - required this.userSlug, - required this.consultantPayment, - required this.duration, - required this.currentDiaryDay, - @JsonKey(name: 'start_date', fromJson: parseDate) required this.startDate, - @JsonKey(name: 'end_date', fromJson: parseDate) required this.endDate, - @JsonKey(name: 'consultant_id') required this.consultantId, - required final List diaries}) - : _diaries = diaries; - - factory _$ConsultationModelImpl.fromJson(Map json) => - _$$ConsultationModelImplFromJson(json); - - @override - final int id; - @override - @JsonKey(name: 'last_state') - final ChosenPlanStateEnum chosenPlanState; - @override - final String planTitle; - @override - final String userName; - @override - final String userSlug; - @override - final double consultantPayment; - @override - final int duration; - @override - final int currentDiaryDay; - @override - @JsonKey(name: 'start_date', fromJson: parseDate) - final DateTime startDate; - @override - @JsonKey(name: 'end_date', fromJson: parseDate) - final DateTime endDate; - @override - @JsonKey(name: 'consultant_id') - final int consultantId; - final List _diaries; - @override - List get diaries { - if (_diaries is EqualUnmodifiableListView) return _diaries; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_diaries); - } - - @override - String toString() { - return 'ConsultationModel(id: $id, chosenPlanState: $chosenPlanState, planTitle: $planTitle, userName: $userName, userSlug: $userSlug, consultantPayment: $consultantPayment, duration: $duration, currentDiaryDay: $currentDiaryDay, startDate: $startDate, endDate: $endDate, consultantId: $consultantId, diaries: $diaries)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$ConsultationModelImpl && - (identical(other.id, id) || other.id == id) && - (identical(other.chosenPlanState, chosenPlanState) || - other.chosenPlanState == chosenPlanState) && - (identical(other.planTitle, planTitle) || - other.planTitle == planTitle) && - (identical(other.userName, userName) || - other.userName == userName) && - (identical(other.userSlug, userSlug) || - other.userSlug == userSlug) && - (identical(other.consultantPayment, consultantPayment) || - other.consultantPayment == consultantPayment) && - (identical(other.duration, duration) || - other.duration == duration) && - (identical(other.currentDiaryDay, currentDiaryDay) || - other.currentDiaryDay == currentDiaryDay) && - (identical(other.startDate, startDate) || - other.startDate == startDate) && - (identical(other.endDate, endDate) || other.endDate == endDate) && - (identical(other.consultantId, consultantId) || - other.consultantId == consultantId) && - const DeepCollectionEquality().equals(other._diaries, _diaries)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - id, - chosenPlanState, - planTitle, - userName, - userSlug, - consultantPayment, - duration, - currentDiaryDay, - startDate, - endDate, - consultantId, - const DeepCollectionEquality().hash(_diaries)); - - /// Create a copy of ConsultationModel - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$ConsultationModelImplCopyWith<_$ConsultationModelImpl> get copyWith => - __$$ConsultationModelImplCopyWithImpl<_$ConsultationModelImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$ConsultationModelImplToJson( - this, - ); - } -} - -abstract class _ConsultationModel implements ConsultationModel { - factory _ConsultationModel( - {required final int id, - @JsonKey(name: 'last_state') - required final ChosenPlanStateEnum chosenPlanState, - required final String planTitle, - required final String userName, - required final String userSlug, - required final double consultantPayment, - required final int duration, - required final int currentDiaryDay, - @JsonKey(name: 'start_date', fromJson: parseDate) - required final DateTime startDate, - @JsonKey(name: 'end_date', fromJson: parseDate) - required final DateTime endDate, - @JsonKey(name: 'consultant_id') required final int consultantId, - required final List diaries}) = _$ConsultationModelImpl; - - factory _ConsultationModel.fromJson(Map json) = - _$ConsultationModelImpl.fromJson; - - @override - int get id; - @override - @JsonKey(name: 'last_state') - ChosenPlanStateEnum get chosenPlanState; - @override - String get planTitle; - @override - String get userName; - @override - String get userSlug; - @override - double get consultantPayment; - @override - int get duration; - @override - int get currentDiaryDay; - @override - @JsonKey(name: 'start_date', fromJson: parseDate) - DateTime get startDate; - @override - @JsonKey(name: 'end_date', fromJson: parseDate) - DateTime get endDate; - @override - @JsonKey(name: 'consultant_id') - int get consultantId; - @override - List get diaries; - - /// Create a copy of ConsultationModel - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$ConsultationModelImplCopyWith<_$ConsultationModelImpl> get copyWith => - throw _privateConstructorUsedError; -} - -DiaryModel _$DiaryModelFromJson(Map json) { - return _DiaryModel.fromJson(json); -} - -/// @nodoc -mixin _$DiaryModel { - int get id => throw _privateConstructorUsedError; - DiaryStateEnum get state => throw _privateConstructorUsedError; - @JsonKey(fromJson: parseUnformattedDate) - DateTime get date => throw _privateConstructorUsedError; - - /// Serializes this DiaryModel to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of DiaryModel - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $DiaryModelCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $DiaryModelCopyWith<$Res> { - factory $DiaryModelCopyWith( - DiaryModel value, $Res Function(DiaryModel) then) = - _$DiaryModelCopyWithImpl<$Res, DiaryModel>; - @useResult - $Res call( - {int id, - DiaryStateEnum state, - @JsonKey(fromJson: parseUnformattedDate) DateTime date}); -} - -/// @nodoc -class _$DiaryModelCopyWithImpl<$Res, $Val extends DiaryModel> - implements $DiaryModelCopyWith<$Res> { - _$DiaryModelCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of DiaryModel - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? id = null, - Object? state = null, - Object? date = null, - }) { - return _then(_value.copyWith( - id: null == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int, - state: null == state - ? _value.state - : state // ignore: cast_nullable_to_non_nullable - as DiaryStateEnum, - date: null == date - ? _value.date - : date // ignore: cast_nullable_to_non_nullable - as DateTime, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$DiaryModelImplCopyWith<$Res> - implements $DiaryModelCopyWith<$Res> { - factory _$$DiaryModelImplCopyWith( - _$DiaryModelImpl value, $Res Function(_$DiaryModelImpl) then) = - __$$DiaryModelImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {int id, - DiaryStateEnum state, - @JsonKey(fromJson: parseUnformattedDate) DateTime date}); -} - -/// @nodoc -class __$$DiaryModelImplCopyWithImpl<$Res> - extends _$DiaryModelCopyWithImpl<$Res, _$DiaryModelImpl> - implements _$$DiaryModelImplCopyWith<$Res> { - __$$DiaryModelImplCopyWithImpl( - _$DiaryModelImpl _value, $Res Function(_$DiaryModelImpl) _then) - : super(_value, _then); - - /// Create a copy of DiaryModel - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? id = null, - Object? state = null, - Object? date = null, - }) { - return _then(_$DiaryModelImpl( - id: null == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int, - state: null == state - ? _value.state - : state // ignore: cast_nullable_to_non_nullable - as DiaryStateEnum, - date: null == date - ? _value.date - : date // ignore: cast_nullable_to_non_nullable - as DateTime, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$DiaryModelImpl implements _DiaryModel { - _$DiaryModelImpl( - {required this.id, - required this.state, - @JsonKey(fromJson: parseUnformattedDate) required this.date}); - - factory _$DiaryModelImpl.fromJson(Map json) => - _$$DiaryModelImplFromJson(json); - - @override - final int id; - @override - final DiaryStateEnum state; - @override - @JsonKey(fromJson: parseUnformattedDate) - final DateTime date; - - @override - String toString() { - return 'DiaryModel(id: $id, state: $state, date: $date)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$DiaryModelImpl && - (identical(other.id, id) || other.id == id) && - (identical(other.state, state) || other.state == state) && - (identical(other.date, date) || other.date == date)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, id, state, date); - - /// Create a copy of DiaryModel - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$DiaryModelImplCopyWith<_$DiaryModelImpl> get copyWith => - __$$DiaryModelImplCopyWithImpl<_$DiaryModelImpl>(this, _$identity); - - @override - Map toJson() { - return _$$DiaryModelImplToJson( - this, - ); - } -} - -abstract class _DiaryModel implements DiaryModel { - factory _DiaryModel( - {required final int id, - required final DiaryStateEnum state, - @JsonKey(fromJson: parseUnformattedDate) - required final DateTime date}) = _$DiaryModelImpl; - - factory _DiaryModel.fromJson(Map json) = - _$DiaryModelImpl.fromJson; - - @override - int get id; - @override - DiaryStateEnum get state; - @override - @JsonKey(fromJson: parseUnformattedDate) - DateTime get date; - - /// Create a copy of DiaryModel - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$DiaryModelImplCopyWith<_$DiaryModelImpl> get copyWith => - throw _privateConstructorUsedError; -} - -ConsultationPaginationModel _$ConsultationPaginationModelFromJson( - Map json) { - return _ConsultationPaginationModel.fromJson(json); -} - -/// @nodoc -mixin _$ConsultationPaginationModel { - List get data => throw _privateConstructorUsedError; - - /// Serializes this ConsultationPaginationModel to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of ConsultationPaginationModel - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $ConsultationPaginationModelCopyWith - get copyWith => throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $ConsultationPaginationModelCopyWith<$Res> { - factory $ConsultationPaginationModelCopyWith( - ConsultationPaginationModel value, - $Res Function(ConsultationPaginationModel) then) = - _$ConsultationPaginationModelCopyWithImpl<$Res, - ConsultationPaginationModel>; - @useResult - $Res call({List data}); -} - -/// @nodoc -class _$ConsultationPaginationModelCopyWithImpl<$Res, - $Val extends ConsultationPaginationModel> - implements $ConsultationPaginationModelCopyWith<$Res> { - _$ConsultationPaginationModelCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of ConsultationPaginationModel - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? data = null, - }) { - return _then(_value.copyWith( - data: null == data - ? _value.data - : data // ignore: cast_nullable_to_non_nullable - as List, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$ConsultationPaginationModelImplCopyWith<$Res> - implements $ConsultationPaginationModelCopyWith<$Res> { - factory _$$ConsultationPaginationModelImplCopyWith( - _$ConsultationPaginationModelImpl value, - $Res Function(_$ConsultationPaginationModelImpl) then) = - __$$ConsultationPaginationModelImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({List data}); -} - -/// @nodoc -class __$$ConsultationPaginationModelImplCopyWithImpl<$Res> - extends _$ConsultationPaginationModelCopyWithImpl<$Res, - _$ConsultationPaginationModelImpl> - implements _$$ConsultationPaginationModelImplCopyWith<$Res> { - __$$ConsultationPaginationModelImplCopyWithImpl( - _$ConsultationPaginationModelImpl _value, - $Res Function(_$ConsultationPaginationModelImpl) _then) - : super(_value, _then); - - /// Create a copy of ConsultationPaginationModel - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? data = null, - }) { - return _then(_$ConsultationPaginationModelImpl( - data: null == data - ? _value._data - : data // ignore: cast_nullable_to_non_nullable - as List, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$ConsultationPaginationModelImpl - implements _ConsultationPaginationModel { - _$ConsultationPaginationModelImpl( - {required final List data}) - : _data = data; - - factory _$ConsultationPaginationModelImpl.fromJson( - Map json) => - _$$ConsultationPaginationModelImplFromJson(json); - - final List _data; - @override - List get data { - if (_data is EqualUnmodifiableListView) return _data; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_data); - } - - @override - String toString() { - return 'ConsultationPaginationModel(data: $data)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$ConsultationPaginationModelImpl && - const DeepCollectionEquality().equals(other._data, _data)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => - Object.hash(runtimeType, const DeepCollectionEquality().hash(_data)); - - /// Create a copy of ConsultationPaginationModel - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$ConsultationPaginationModelImplCopyWith<_$ConsultationPaginationModelImpl> - get copyWith => __$$ConsultationPaginationModelImplCopyWithImpl< - _$ConsultationPaginationModelImpl>(this, _$identity); - - @override - Map toJson() { - return _$$ConsultationPaginationModelImplToJson( - this, - ); - } -} - -abstract class _ConsultationPaginationModel - implements ConsultationPaginationModel { - factory _ConsultationPaginationModel( - {required final List data}) = - _$ConsultationPaginationModelImpl; - - factory _ConsultationPaginationModel.fromJson(Map json) = - _$ConsultationPaginationModelImpl.fromJson; - - @override - List get data; - - /// Create a copy of ConsultationPaginationModel - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$ConsultationPaginationModelImplCopyWith<_$ConsultationPaginationModelImpl> - get copyWith => throw _privateConstructorUsedError; -} diff --git a/lib/features/consultations/models/consultation_model.g.dart b/lib/features/consultations/models/consultation_model.g.dart deleted file mode 100644 index ff93e365..00000000 --- a/lib/features/consultations/models/consultation_model.g.dart +++ /dev/null @@ -1,100 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'consultation_model.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$ConsultationModelImpl _$$ConsultationModelImplFromJson( - Map json) => - _$ConsultationModelImpl( - id: (json['id'] as num).toInt(), - chosenPlanState: - $enumDecode(_$ChosenPlanStateEnumEnumMap, json['last_state']), - planTitle: json['planTitle'] as String, - userName: json['userName'] as String, - userSlug: json['userSlug'] as String, - consultantPayment: (json['consultantPayment'] as num).toDouble(), - duration: (json['duration'] as num).toInt(), - currentDiaryDay: (json['currentDiaryDay'] as num).toInt(), - startDate: parseDate(json['start_date'] as String), - endDate: parseDate(json['end_date'] as String), - consultantId: (json['consultant_id'] as num).toInt(), - diaries: (json['diaries'] as List) - .map((e) => DiaryModel.fromJson(e as Map)) - .toList(), - ); - -Map _$$ConsultationModelImplToJson( - _$ConsultationModelImpl instance) => - { - 'id': instance.id, - 'last_state': _$ChosenPlanStateEnumEnumMap[instance.chosenPlanState]!, - 'planTitle': instance.planTitle, - 'userName': instance.userName, - 'userSlug': instance.userSlug, - 'consultantPayment': instance.consultantPayment, - 'duration': instance.duration, - 'currentDiaryDay': instance.currentDiaryDay, - 'start_date': instance.startDate.toIso8601String(), - 'end_date': instance.endDate.toIso8601String(), - 'consultant_id': instance.consultantId, - 'diaries': instance.diaries, - }; - -const _$ChosenPlanStateEnumEnumMap = { - ChosenPlanStateEnum.stateInited: 'initied', - ChosenPlanStateEnum.stateStartDateHasBeenSet: 'start_date_has_been_set', - ChosenPlanStateEnum.stateInCoordination: 'in_coordination', - ChosenPlanStateEnum.stateStarted: 'started', - ChosenPlanStateEnum.stateInProgress: 'in_progress', - ChosenPlanStateEnum.statePendingPayment: 'pending_payment', - ChosenPlanStateEnum.stateConfirmedByConsultant: 'confirmed_by_consultant', - ChosenPlanStateEnum.statePaid: 'paid', - ChosenPlanStateEnum.stateCanceledByTheInitiator: 'canceled_by_the_initiator', - ChosenPlanStateEnum.stateLastDayStarted: 'last_day_started', - ChosenPlanStateEnum.stateLastDayFinished: 'last_day_finished', - ChosenPlanStateEnum.stateComplained: 'complained', - ChosenPlanStateEnum.stateDone: 'done', - ChosenPlanStateEnum.stateChargeWasCollected: 'charge_was_collected', -}; - -_$DiaryModelImpl _$$DiaryModelImplFromJson(Map json) => - _$DiaryModelImpl( - id: (json['id'] as num).toInt(), - state: $enumDecode(_$DiaryStateEnumEnumMap, json['state']), - date: parseUnformattedDate(json['date'] as String), - ); - -Map _$$DiaryModelImplToJson(_$DiaryModelImpl instance) => - { - 'id': instance.id, - 'state': _$DiaryStateEnumEnumMap[instance.state]!, - 'date': instance.date.toIso8601String(), - }; - -const _$DiaryStateEnumEnumMap = { - DiaryStateEnum.init: 'init', - DiaryStateEnum.started: 'started', - DiaryStateEnum.finished: 'finished', - DiaryStateEnum.commented: 'commented', - DiaryStateEnum.reviewedByUser: 'reviewed_by_user', - DiaryStateEnum.lastDayStarted: 'last_day_started', - DiaryStateEnum.lastDayFinished: 'last_day_finished', - DiaryStateEnum.lastDayCommented: 'last_day_commented', -}; - -_$ConsultationPaginationModelImpl _$$ConsultationPaginationModelImplFromJson( - Map json) => - _$ConsultationPaginationModelImpl( - data: (json['data'] as List) - .map((e) => ConsultationModel.fromJson(e as Map)) - .toList(), - ); - -Map _$$ConsultationPaginationModelImplToJson( - _$ConsultationPaginationModelImpl instance) => - { - 'data': instance.data, - }; diff --git a/lib/features/consultations/presentation/cubit/consultations_state.freezed.dart b/lib/features/consultations/presentation/cubit/consultations_state.freezed.dart deleted file mode 100644 index 7d4203e5..00000000 --- a/lib/features/consultations/presentation/cubit/consultations_state.freezed.dart +++ /dev/null @@ -1,175 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'consultations_state.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -/// @nodoc -mixin _$ConsultationsState { - BaseState get state => throw _privateConstructorUsedError; - List get consultations => - throw _privateConstructorUsedError; - - /// Create a copy of ConsultationsState - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $ConsultationsStateCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $ConsultationsStateCopyWith<$Res> { - factory $ConsultationsStateCopyWith( - ConsultationsState value, $Res Function(ConsultationsState) then) = - _$ConsultationsStateCopyWithImpl<$Res, ConsultationsState>; - @useResult - $Res call({BaseState state, List consultations}); -} - -/// @nodoc -class _$ConsultationsStateCopyWithImpl<$Res, $Val extends ConsultationsState> - implements $ConsultationsStateCopyWith<$Res> { - _$ConsultationsStateCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of ConsultationsState - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? state = null, - Object? consultations = null, - }) { - return _then(_value.copyWith( - state: null == state - ? _value.state - : state // ignore: cast_nullable_to_non_nullable - as BaseState, - consultations: null == consultations - ? _value.consultations - : consultations // ignore: cast_nullable_to_non_nullable - as List, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$ConsultationsStateImplCopyWith<$Res> - implements $ConsultationsStateCopyWith<$Res> { - factory _$$ConsultationsStateImplCopyWith(_$ConsultationsStateImpl value, - $Res Function(_$ConsultationsStateImpl) then) = - __$$ConsultationsStateImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({BaseState state, List consultations}); -} - -/// @nodoc -class __$$ConsultationsStateImplCopyWithImpl<$Res> - extends _$ConsultationsStateCopyWithImpl<$Res, _$ConsultationsStateImpl> - implements _$$ConsultationsStateImplCopyWith<$Res> { - __$$ConsultationsStateImplCopyWithImpl(_$ConsultationsStateImpl _value, - $Res Function(_$ConsultationsStateImpl) _then) - : super(_value, _then); - - /// Create a copy of ConsultationsState - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? state = null, - Object? consultations = null, - }) { - return _then(_$ConsultationsStateImpl( - state: null == state - ? _value.state - : state // ignore: cast_nullable_to_non_nullable - as BaseState, - consultations: null == consultations - ? _value._consultations - : consultations // ignore: cast_nullable_to_non_nullable - as List, - )); - } -} - -/// @nodoc - -class _$ConsultationsStateImpl implements _ConsultationsState { - _$ConsultationsStateImpl( - {this.state = BaseState.initial, - final List consultations = const []}) - : _consultations = consultations; - - @override - @JsonKey() - final BaseState state; - final List _consultations; - @override - @JsonKey() - List get consultations { - if (_consultations is EqualUnmodifiableListView) return _consultations; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_consultations); - } - - @override - String toString() { - return 'ConsultationsState(state: $state, consultations: $consultations)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$ConsultationsStateImpl && - (identical(other.state, state) || other.state == state) && - const DeepCollectionEquality() - .equals(other._consultations, _consultations)); - } - - @override - int get hashCode => Object.hash( - runtimeType, state, const DeepCollectionEquality().hash(_consultations)); - - /// Create a copy of ConsultationsState - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$ConsultationsStateImplCopyWith<_$ConsultationsStateImpl> get copyWith => - __$$ConsultationsStateImplCopyWithImpl<_$ConsultationsStateImpl>( - this, _$identity); -} - -abstract class _ConsultationsState implements ConsultationsState { - factory _ConsultationsState( - {final BaseState state, - final List consultations}) = _$ConsultationsStateImpl; - - @override - BaseState get state; - @override - List get consultations; - - /// Create a copy of ConsultationsState - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$ConsultationsStateImplCopyWith<_$ConsultationsStateImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/features/consultations/services/consultation_service.g.dart b/lib/features/consultations/services/consultation_service.g.dart deleted file mode 100644 index ff0edd2b..00000000 --- a/lib/features/consultations/services/consultation_service.g.dart +++ /dev/null @@ -1,73 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'consultation_service.dart'; - -// ************************************************************************** -// RetrofitGenerator -// ************************************************************************** - -// ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers,unused_element,unnecessary_string_interpolations - -class _ConsultationService implements ConsultationService { - _ConsultationService(this._dio, {this.baseUrl, this.errorLogger}); - - final Dio _dio; - - String? baseUrl; - - final ParseErrorLogger? errorLogger; - - @override - Future getConsultations() async { - final _extra = {}; - final queryParameters = {}; - final _headers = {}; - const Map? _data = null; - final _options = _setStreamType( - Options(method: 'GET', headers: _headers, extra: _extra) - .compose( - _dio.options, - '/api/consultations', - queryParameters: queryParameters, - data: _data, - ) - .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), - ); - final _result = await _dio.fetch>(_options); - late ConsultationPaginationModel _value; - try { - _value = ConsultationPaginationModel.fromJson(_result.data!); - } on Object catch (e, s) { - errorLogger?.logError(e, s, _options); - rethrow; - } - return _value; - } - - RequestOptions _setStreamType(RequestOptions requestOptions) { - if (T != dynamic && - !(requestOptions.responseType == ResponseType.bytes || - requestOptions.responseType == ResponseType.stream)) { - if (T == String) { - requestOptions.responseType = ResponseType.plain; - } else { - requestOptions.responseType = ResponseType.json; - } - } - return requestOptions; - } - - String _combineBaseUrls(String dioBaseUrl, String? baseUrl) { - if (baseUrl == null || baseUrl.trim().isEmpty) { - return dioBaseUrl; - } - - final url = Uri.parse(baseUrl); - - if (url.isAbsolute) { - return url.toString(); - } - - return Uri.parse(dioBaseUrl).resolveUri(url).toString(); - } -} diff --git a/lib/features/diary/models/consultant_comment_model.freezed.dart b/lib/features/diary/models/consultant_comment_model.freezed.dart deleted file mode 100644 index 93b59f59..00000000 --- a/lib/features/diary/models/consultant_comment_model.freezed.dart +++ /dev/null @@ -1,581 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'consultant_comment_model.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -ConsultantCommentModel _$ConsultantCommentModelFromJson( - Map json) { - return _ConsultantCommentModel.fromJson(json); -} - -/// @nodoc -mixin _$ConsultantCommentModel { - String? get comment => throw _privateConstructorUsedError; - @JsonKey(name: 'meals') - Map get mealsComments => - throw _privateConstructorUsedError; - List get portions => throw _privateConstructorUsedError; - - /// Serializes this ConsultantCommentModel to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of ConsultantCommentModel - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $ConsultantCommentModelCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $ConsultantCommentModelCopyWith<$Res> { - factory $ConsultantCommentModelCopyWith(ConsultantCommentModel value, - $Res Function(ConsultantCommentModel) then) = - _$ConsultantCommentModelCopyWithImpl<$Res, ConsultantCommentModel>; - @useResult - $Res call( - {String? comment, - @JsonKey(name: 'meals') Map mealsComments, - List portions}); -} - -/// @nodoc -class _$ConsultantCommentModelCopyWithImpl<$Res, - $Val extends ConsultantCommentModel> - implements $ConsultantCommentModelCopyWith<$Res> { - _$ConsultantCommentModelCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of ConsultantCommentModel - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? comment = freezed, - Object? mealsComments = null, - Object? portions = null, - }) { - return _then(_value.copyWith( - comment: freezed == comment - ? _value.comment - : comment // ignore: cast_nullable_to_non_nullable - as String?, - mealsComments: null == mealsComments - ? _value.mealsComments - : mealsComments // ignore: cast_nullable_to_non_nullable - as Map, - portions: null == portions - ? _value.portions - : portions // ignore: cast_nullable_to_non_nullable - as List, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$ConsultantCommentModelImplCopyWith<$Res> - implements $ConsultantCommentModelCopyWith<$Res> { - factory _$$ConsultantCommentModelImplCopyWith( - _$ConsultantCommentModelImpl value, - $Res Function(_$ConsultantCommentModelImpl) then) = - __$$ConsultantCommentModelImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {String? comment, - @JsonKey(name: 'meals') Map mealsComments, - List portions}); -} - -/// @nodoc -class __$$ConsultantCommentModelImplCopyWithImpl<$Res> - extends _$ConsultantCommentModelCopyWithImpl<$Res, - _$ConsultantCommentModelImpl> - implements _$$ConsultantCommentModelImplCopyWith<$Res> { - __$$ConsultantCommentModelImplCopyWithImpl( - _$ConsultantCommentModelImpl _value, - $Res Function(_$ConsultantCommentModelImpl) _then) - : super(_value, _then); - - /// Create a copy of ConsultantCommentModel - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? comment = freezed, - Object? mealsComments = null, - Object? portions = null, - }) { - return _then(_$ConsultantCommentModelImpl( - comment: freezed == comment - ? _value.comment - : comment // ignore: cast_nullable_to_non_nullable - as String?, - mealsComments: null == mealsComments - ? _value._mealsComments - : mealsComments // ignore: cast_nullable_to_non_nullable - as Map, - portions: null == portions - ? _value._portions - : portions // ignore: cast_nullable_to_non_nullable - as List, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$ConsultantCommentModelImpl implements _ConsultantCommentModel { - _$ConsultantCommentModelImpl( - {this.comment, - @JsonKey(name: 'meals') - final Map mealsComments = const {}, - final List portions = const []}) - : _mealsComments = mealsComments, - _portions = portions; - - factory _$ConsultantCommentModelImpl.fromJson(Map json) => - _$$ConsultantCommentModelImplFromJson(json); - - @override - final String? comment; - final Map _mealsComments; - @override - @JsonKey(name: 'meals') - Map get mealsComments { - if (_mealsComments is EqualUnmodifiableMapView) return _mealsComments; - // ignore: implicit_dynamic_type - return EqualUnmodifiableMapView(_mealsComments); - } - - final List _portions; - @override - @JsonKey() - List get portions { - if (_portions is EqualUnmodifiableListView) return _portions; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_portions); - } - - @override - String toString() { - return 'ConsultantCommentModel(comment: $comment, mealsComments: $mealsComments, portions: $portions)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$ConsultantCommentModelImpl && - (identical(other.comment, comment) || other.comment == comment) && - const DeepCollectionEquality() - .equals(other._mealsComments, _mealsComments) && - const DeepCollectionEquality().equals(other._portions, _portions)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - comment, - const DeepCollectionEquality().hash(_mealsComments), - const DeepCollectionEquality().hash(_portions)); - - /// Create a copy of ConsultantCommentModel - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$ConsultantCommentModelImplCopyWith<_$ConsultantCommentModelImpl> - get copyWith => __$$ConsultantCommentModelImplCopyWithImpl< - _$ConsultantCommentModelImpl>(this, _$identity); - - @override - Map toJson() { - return _$$ConsultantCommentModelImplToJson( - this, - ); - } -} - -abstract class _ConsultantCommentModel implements ConsultantCommentModel { - factory _ConsultantCommentModel( - {final String? comment, - @JsonKey(name: 'meals') final Map mealsComments, - final List portions}) = _$ConsultantCommentModelImpl; - - factory _ConsultantCommentModel.fromJson(Map json) = - _$ConsultantCommentModelImpl.fromJson; - - @override - String? get comment; - @override - @JsonKey(name: 'meals') - Map get mealsComments; - @override - List get portions; - - /// Create a copy of ConsultantCommentModel - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$ConsultantCommentModelImplCopyWith<_$ConsultantCommentModelImpl> - get copyWith => throw _privateConstructorUsedError; -} - -PortionCommentModel _$PortionCommentModelFromJson(Map json) { - return _PortionCommentModel.fromJson(json); -} - -/// @nodoc -mixin _$PortionCommentModel { - String get comment => throw _privateConstructorUsedError; - int get id => throw _privateConstructorUsedError; - - /// Serializes this PortionCommentModel to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of PortionCommentModel - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $PortionCommentModelCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $PortionCommentModelCopyWith<$Res> { - factory $PortionCommentModelCopyWith( - PortionCommentModel value, $Res Function(PortionCommentModel) then) = - _$PortionCommentModelCopyWithImpl<$Res, PortionCommentModel>; - @useResult - $Res call({String comment, int id}); -} - -/// @nodoc -class _$PortionCommentModelCopyWithImpl<$Res, $Val extends PortionCommentModel> - implements $PortionCommentModelCopyWith<$Res> { - _$PortionCommentModelCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of PortionCommentModel - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? comment = null, - Object? id = null, - }) { - return _then(_value.copyWith( - comment: null == comment - ? _value.comment - : comment // ignore: cast_nullable_to_non_nullable - as String, - id: null == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$PortionCommentModelImplCopyWith<$Res> - implements $PortionCommentModelCopyWith<$Res> { - factory _$$PortionCommentModelImplCopyWith(_$PortionCommentModelImpl value, - $Res Function(_$PortionCommentModelImpl) then) = - __$$PortionCommentModelImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({String comment, int id}); -} - -/// @nodoc -class __$$PortionCommentModelImplCopyWithImpl<$Res> - extends _$PortionCommentModelCopyWithImpl<$Res, _$PortionCommentModelImpl> - implements _$$PortionCommentModelImplCopyWith<$Res> { - __$$PortionCommentModelImplCopyWithImpl(_$PortionCommentModelImpl _value, - $Res Function(_$PortionCommentModelImpl) _then) - : super(_value, _then); - - /// Create a copy of PortionCommentModel - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? comment = null, - Object? id = null, - }) { - return _then(_$PortionCommentModelImpl( - comment: null == comment - ? _value.comment - : comment // ignore: cast_nullable_to_non_nullable - as String, - id: null == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$PortionCommentModelImpl implements _PortionCommentModel { - _$PortionCommentModelImpl({required this.comment, required this.id}); - - factory _$PortionCommentModelImpl.fromJson(Map json) => - _$$PortionCommentModelImplFromJson(json); - - @override - final String comment; - @override - final int id; - - @override - String toString() { - return 'PortionCommentModel(comment: $comment, id: $id)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$PortionCommentModelImpl && - (identical(other.comment, comment) || other.comment == comment) && - (identical(other.id, id) || other.id == id)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, comment, id); - - /// Create a copy of PortionCommentModel - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$PortionCommentModelImplCopyWith<_$PortionCommentModelImpl> get copyWith => - __$$PortionCommentModelImplCopyWithImpl<_$PortionCommentModelImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$PortionCommentModelImplToJson( - this, - ); - } -} - -abstract class _PortionCommentModel implements PortionCommentModel { - factory _PortionCommentModel( - {required final String comment, - required final int id}) = _$PortionCommentModelImpl; - - factory _PortionCommentModel.fromJson(Map json) = - _$PortionCommentModelImpl.fromJson; - - @override - String get comment; - @override - int get id; - - /// Create a copy of PortionCommentModel - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$PortionCommentModelImplCopyWith<_$PortionCommentModelImpl> get copyWith => - throw _privateConstructorUsedError; -} - -MealCommentModel _$MealCommentModelFromJson(Map json) { - return _MealCommentModel.fromJson(json); -} - -/// @nodoc -mixin _$MealCommentModel { - String get comment => throw _privateConstructorUsedError; - @JsonKey(name: 'type_no') - int get typeNumber => throw _privateConstructorUsedError; - - /// Serializes this MealCommentModel to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of MealCommentModel - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $MealCommentModelCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $MealCommentModelCopyWith<$Res> { - factory $MealCommentModelCopyWith( - MealCommentModel value, $Res Function(MealCommentModel) then) = - _$MealCommentModelCopyWithImpl<$Res, MealCommentModel>; - @useResult - $Res call({String comment, @JsonKey(name: 'type_no') int typeNumber}); -} - -/// @nodoc -class _$MealCommentModelCopyWithImpl<$Res, $Val extends MealCommentModel> - implements $MealCommentModelCopyWith<$Res> { - _$MealCommentModelCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of MealCommentModel - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? comment = null, - Object? typeNumber = null, - }) { - return _then(_value.copyWith( - comment: null == comment - ? _value.comment - : comment // ignore: cast_nullable_to_non_nullable - as String, - typeNumber: null == typeNumber - ? _value.typeNumber - : typeNumber // ignore: cast_nullable_to_non_nullable - as int, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$MealCommentModelImplCopyWith<$Res> - implements $MealCommentModelCopyWith<$Res> { - factory _$$MealCommentModelImplCopyWith(_$MealCommentModelImpl value, - $Res Function(_$MealCommentModelImpl) then) = - __$$MealCommentModelImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({String comment, @JsonKey(name: 'type_no') int typeNumber}); -} - -/// @nodoc -class __$$MealCommentModelImplCopyWithImpl<$Res> - extends _$MealCommentModelCopyWithImpl<$Res, _$MealCommentModelImpl> - implements _$$MealCommentModelImplCopyWith<$Res> { - __$$MealCommentModelImplCopyWithImpl(_$MealCommentModelImpl _value, - $Res Function(_$MealCommentModelImpl) _then) - : super(_value, _then); - - /// Create a copy of MealCommentModel - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? comment = null, - Object? typeNumber = null, - }) { - return _then(_$MealCommentModelImpl( - comment: null == comment - ? _value.comment - : comment // ignore: cast_nullable_to_non_nullable - as String, - typeNumber: null == typeNumber - ? _value.typeNumber - : typeNumber // ignore: cast_nullable_to_non_nullable - as int, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$MealCommentModelImpl implements _MealCommentModel { - _$MealCommentModelImpl( - {required this.comment, - @JsonKey(name: 'type_no') required this.typeNumber}); - - factory _$MealCommentModelImpl.fromJson(Map json) => - _$$MealCommentModelImplFromJson(json); - - @override - final String comment; - @override - @JsonKey(name: 'type_no') - final int typeNumber; - - @override - String toString() { - return 'MealCommentModel(comment: $comment, typeNumber: $typeNumber)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$MealCommentModelImpl && - (identical(other.comment, comment) || other.comment == comment) && - (identical(other.typeNumber, typeNumber) || - other.typeNumber == typeNumber)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, comment, typeNumber); - - /// Create a copy of MealCommentModel - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$MealCommentModelImplCopyWith<_$MealCommentModelImpl> get copyWith => - __$$MealCommentModelImplCopyWithImpl<_$MealCommentModelImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$MealCommentModelImplToJson( - this, - ); - } -} - -abstract class _MealCommentModel implements MealCommentModel { - factory _MealCommentModel( - {required final String comment, - @JsonKey(name: 'type_no') required final int typeNumber}) = - _$MealCommentModelImpl; - - factory _MealCommentModel.fromJson(Map json) = - _$MealCommentModelImpl.fromJson; - - @override - String get comment; - @override - @JsonKey(name: 'type_no') - int get typeNumber; - - /// Create a copy of MealCommentModel - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$MealCommentModelImplCopyWith<_$MealCommentModelImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/features/diary/models/consultant_comment_model.g.dart b/lib/features/diary/models/consultant_comment_model.g.dart deleted file mode 100644 index 4ea3943a..00000000 --- a/lib/features/diary/models/consultant_comment_model.g.dart +++ /dev/null @@ -1,59 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'consultant_comment_model.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$ConsultantCommentModelImpl _$$ConsultantCommentModelImplFromJson( - Map json) => - _$ConsultantCommentModelImpl( - comment: json['comment'] as String?, - mealsComments: (json['meals'] as Map?)?.map( - (k, e) => MapEntry( - k, MealCommentModel.fromJson(e as Map)), - ) ?? - const {}, - portions: (json['portions'] as List?) - ?.map((e) => - PortionCommentModel.fromJson(e as Map)) - .toList() ?? - const [], - ); - -Map _$$ConsultantCommentModelImplToJson( - _$ConsultantCommentModelImpl instance) => - { - 'comment': instance.comment, - 'meals': instance.mealsComments, - 'portions': instance.portions, - }; - -_$PortionCommentModelImpl _$$PortionCommentModelImplFromJson( - Map json) => - _$PortionCommentModelImpl( - comment: json['comment'] as String, - id: (json['id'] as num).toInt(), - ); - -Map _$$PortionCommentModelImplToJson( - _$PortionCommentModelImpl instance) => - { - 'comment': instance.comment, - 'id': instance.id, - }; - -_$MealCommentModelImpl _$$MealCommentModelImplFromJson( - Map json) => - _$MealCommentModelImpl( - comment: json['comment'] as String, - typeNumber: (json['type_no'] as num).toInt(), - ); - -Map _$$MealCommentModelImplToJson( - _$MealCommentModelImpl instance) => - { - 'comment': instance.comment, - 'type_no': instance.typeNumber, - }; diff --git a/lib/features/diary/models/consultant_review_model.freezed.dart b/lib/features/diary/models/consultant_review_model.freezed.dart deleted file mode 100644 index 96310b65..00000000 --- a/lib/features/diary/models/consultant_review_model.freezed.dart +++ /dev/null @@ -1,796 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'consultant_review_model.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -ConsultantReviewModel _$ConsultantReviewModelFromJson( - Map json) { - return _ConsultantReviewModel.fromJson(json); -} - -/// @nodoc -mixin _$ConsultantReviewModel { - String get planName => throw _privateConstructorUsedError; - int get planDuration => throw _privateConstructorUsedError; - int get currentPlanDay => throw _privateConstructorUsedError; - ConsultantComments get consultantComments => - throw _privateConstructorUsedError; - - /// Serializes this ConsultantReviewModel to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of ConsultantReviewModel - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $ConsultantReviewModelCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $ConsultantReviewModelCopyWith<$Res> { - factory $ConsultantReviewModelCopyWith(ConsultantReviewModel value, - $Res Function(ConsultantReviewModel) then) = - _$ConsultantReviewModelCopyWithImpl<$Res, ConsultantReviewModel>; - @useResult - $Res call( - {String planName, - int planDuration, - int currentPlanDay, - ConsultantComments consultantComments}); - - $ConsultantCommentsCopyWith<$Res> get consultantComments; -} - -/// @nodoc -class _$ConsultantReviewModelCopyWithImpl<$Res, - $Val extends ConsultantReviewModel> - implements $ConsultantReviewModelCopyWith<$Res> { - _$ConsultantReviewModelCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of ConsultantReviewModel - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? planName = null, - Object? planDuration = null, - Object? currentPlanDay = null, - Object? consultantComments = null, - }) { - return _then(_value.copyWith( - planName: null == planName - ? _value.planName - : planName // ignore: cast_nullable_to_non_nullable - as String, - planDuration: null == planDuration - ? _value.planDuration - : planDuration // ignore: cast_nullable_to_non_nullable - as int, - currentPlanDay: null == currentPlanDay - ? _value.currentPlanDay - : currentPlanDay // ignore: cast_nullable_to_non_nullable - as int, - consultantComments: null == consultantComments - ? _value.consultantComments - : consultantComments // ignore: cast_nullable_to_non_nullable - as ConsultantComments, - ) as $Val); - } - - /// Create a copy of ConsultantReviewModel - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $ConsultantCommentsCopyWith<$Res> get consultantComments { - return $ConsultantCommentsCopyWith<$Res>(_value.consultantComments, - (value) { - return _then(_value.copyWith(consultantComments: value) as $Val); - }); - } -} - -/// @nodoc -abstract class _$$ConsultantReviewModelImplCopyWith<$Res> - implements $ConsultantReviewModelCopyWith<$Res> { - factory _$$ConsultantReviewModelImplCopyWith( - _$ConsultantReviewModelImpl value, - $Res Function(_$ConsultantReviewModelImpl) then) = - __$$ConsultantReviewModelImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {String planName, - int planDuration, - int currentPlanDay, - ConsultantComments consultantComments}); - - @override - $ConsultantCommentsCopyWith<$Res> get consultantComments; -} - -/// @nodoc -class __$$ConsultantReviewModelImplCopyWithImpl<$Res> - extends _$ConsultantReviewModelCopyWithImpl<$Res, - _$ConsultantReviewModelImpl> - implements _$$ConsultantReviewModelImplCopyWith<$Res> { - __$$ConsultantReviewModelImplCopyWithImpl(_$ConsultantReviewModelImpl _value, - $Res Function(_$ConsultantReviewModelImpl) _then) - : super(_value, _then); - - /// Create a copy of ConsultantReviewModel - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? planName = null, - Object? planDuration = null, - Object? currentPlanDay = null, - Object? consultantComments = null, - }) { - return _then(_$ConsultantReviewModelImpl( - planName: null == planName - ? _value.planName - : planName // ignore: cast_nullable_to_non_nullable - as String, - planDuration: null == planDuration - ? _value.planDuration - : planDuration // ignore: cast_nullable_to_non_nullable - as int, - currentPlanDay: null == currentPlanDay - ? _value.currentPlanDay - : currentPlanDay // ignore: cast_nullable_to_non_nullable - as int, - consultantComments: null == consultantComments - ? _value.consultantComments - : consultantComments // ignore: cast_nullable_to_non_nullable - as ConsultantComments, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$ConsultantReviewModelImpl implements _ConsultantReviewModel { - _$ConsultantReviewModelImpl( - {required this.planName, - required this.planDuration, - required this.currentPlanDay, - required this.consultantComments}); - - factory _$ConsultantReviewModelImpl.fromJson(Map json) => - _$$ConsultantReviewModelImplFromJson(json); - - @override - final String planName; - @override - final int planDuration; - @override - final int currentPlanDay; - @override - final ConsultantComments consultantComments; - - @override - String toString() { - return 'ConsultantReviewModel(planName: $planName, planDuration: $planDuration, currentPlanDay: $currentPlanDay, consultantComments: $consultantComments)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$ConsultantReviewModelImpl && - (identical(other.planName, planName) || - other.planName == planName) && - (identical(other.planDuration, planDuration) || - other.planDuration == planDuration) && - (identical(other.currentPlanDay, currentPlanDay) || - other.currentPlanDay == currentPlanDay) && - (identical(other.consultantComments, consultantComments) || - other.consultantComments == consultantComments)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, planName, planDuration, currentPlanDay, consultantComments); - - /// Create a copy of ConsultantReviewModel - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$ConsultantReviewModelImplCopyWith<_$ConsultantReviewModelImpl> - get copyWith => __$$ConsultantReviewModelImplCopyWithImpl< - _$ConsultantReviewModelImpl>(this, _$identity); - - @override - Map toJson() { - return _$$ConsultantReviewModelImplToJson( - this, - ); - } -} - -abstract class _ConsultantReviewModel implements ConsultantReviewModel { - factory _ConsultantReviewModel( - {required final String planName, - required final int planDuration, - required final int currentPlanDay, - required final ConsultantComments consultantComments}) = - _$ConsultantReviewModelImpl; - - factory _ConsultantReviewModel.fromJson(Map json) = - _$ConsultantReviewModelImpl.fromJson; - - @override - String get planName; - @override - int get planDuration; - @override - int get currentPlanDay; - @override - ConsultantComments get consultantComments; - - /// Create a copy of ConsultantReviewModel - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$ConsultantReviewModelImplCopyWith<_$ConsultantReviewModelImpl> - get copyWith => throw _privateConstructorUsedError; -} - -ConsultantComments _$ConsultantCommentsFromJson(Map json) { - return _ConsultantComments.fromJson(json); -} - -/// @nodoc -mixin _$ConsultantComments { - String? get dayComment => throw _privateConstructorUsedError; - List get mealsComments => throw _privateConstructorUsedError; - List get portionsComments => - throw _privateConstructorUsedError; - - /// Serializes this ConsultantComments to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of ConsultantComments - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $ConsultantCommentsCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $ConsultantCommentsCopyWith<$Res> { - factory $ConsultantCommentsCopyWith( - ConsultantComments value, $Res Function(ConsultantComments) then) = - _$ConsultantCommentsCopyWithImpl<$Res, ConsultantComments>; - @useResult - $Res call( - {String? dayComment, - List mealsComments, - List portionsComments}); -} - -/// @nodoc -class _$ConsultantCommentsCopyWithImpl<$Res, $Val extends ConsultantComments> - implements $ConsultantCommentsCopyWith<$Res> { - _$ConsultantCommentsCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of ConsultantComments - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? dayComment = freezed, - Object? mealsComments = null, - Object? portionsComments = null, - }) { - return _then(_value.copyWith( - dayComment: freezed == dayComment - ? _value.dayComment - : dayComment // ignore: cast_nullable_to_non_nullable - as String?, - mealsComments: null == mealsComments - ? _value.mealsComments - : mealsComments // ignore: cast_nullable_to_non_nullable - as List, - portionsComments: null == portionsComments - ? _value.portionsComments - : portionsComments // ignore: cast_nullable_to_non_nullable - as List, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$ConsultantCommentsImplCopyWith<$Res> - implements $ConsultantCommentsCopyWith<$Res> { - factory _$$ConsultantCommentsImplCopyWith(_$ConsultantCommentsImpl value, - $Res Function(_$ConsultantCommentsImpl) then) = - __$$ConsultantCommentsImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {String? dayComment, - List mealsComments, - List portionsComments}); -} - -/// @nodoc -class __$$ConsultantCommentsImplCopyWithImpl<$Res> - extends _$ConsultantCommentsCopyWithImpl<$Res, _$ConsultantCommentsImpl> - implements _$$ConsultantCommentsImplCopyWith<$Res> { - __$$ConsultantCommentsImplCopyWithImpl(_$ConsultantCommentsImpl _value, - $Res Function(_$ConsultantCommentsImpl) _then) - : super(_value, _then); - - /// Create a copy of ConsultantComments - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? dayComment = freezed, - Object? mealsComments = null, - Object? portionsComments = null, - }) { - return _then(_$ConsultantCommentsImpl( - dayComment: freezed == dayComment - ? _value.dayComment - : dayComment // ignore: cast_nullable_to_non_nullable - as String?, - mealsComments: null == mealsComments - ? _value._mealsComments - : mealsComments // ignore: cast_nullable_to_non_nullable - as List, - portionsComments: null == portionsComments - ? _value._portionsComments - : portionsComments // ignore: cast_nullable_to_non_nullable - as List, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$ConsultantCommentsImpl implements _ConsultantComments { - _$ConsultantCommentsImpl( - {required this.dayComment, - required final List mealsComments, - required final List portionsComments}) - : _mealsComments = mealsComments, - _portionsComments = portionsComments; - - factory _$ConsultantCommentsImpl.fromJson(Map json) => - _$$ConsultantCommentsImplFromJson(json); - - @override - final String? dayComment; - final List _mealsComments; - @override - List get mealsComments { - if (_mealsComments is EqualUnmodifiableListView) return _mealsComments; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_mealsComments); - } - - final List _portionsComments; - @override - List get portionsComments { - if (_portionsComments is EqualUnmodifiableListView) - return _portionsComments; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_portionsComments); - } - - @override - String toString() { - return 'ConsultantComments(dayComment: $dayComment, mealsComments: $mealsComments, portionsComments: $portionsComments)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$ConsultantCommentsImpl && - (identical(other.dayComment, dayComment) || - other.dayComment == dayComment) && - const DeepCollectionEquality() - .equals(other._mealsComments, _mealsComments) && - const DeepCollectionEquality() - .equals(other._portionsComments, _portionsComments)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - dayComment, - const DeepCollectionEquality().hash(_mealsComments), - const DeepCollectionEquality().hash(_portionsComments)); - - /// Create a copy of ConsultantComments - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$ConsultantCommentsImplCopyWith<_$ConsultantCommentsImpl> get copyWith => - __$$ConsultantCommentsImplCopyWithImpl<_$ConsultantCommentsImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$ConsultantCommentsImplToJson( - this, - ); - } -} - -abstract class _ConsultantComments implements ConsultantComments { - factory _ConsultantComments( - {required final String? dayComment, - required final List mealsComments, - required final List portionsComments}) = - _$ConsultantCommentsImpl; - - factory _ConsultantComments.fromJson(Map json) = - _$ConsultantCommentsImpl.fromJson; - - @override - String? get dayComment; - @override - List get mealsComments; - @override - List get portionsComments; - - /// Create a copy of ConsultantComments - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$ConsultantCommentsImplCopyWith<_$ConsultantCommentsImpl> get copyWith => - throw _privateConstructorUsedError; -} - -MealComment _$MealCommentFromJson(Map json) { - return _MealComment.fromJson(json); -} - -/// @nodoc -mixin _$MealComment { - String get comment => throw _privateConstructorUsedError; - - /// Serializes this MealComment to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of MealComment - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $MealCommentCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $MealCommentCopyWith<$Res> { - factory $MealCommentCopyWith( - MealComment value, $Res Function(MealComment) then) = - _$MealCommentCopyWithImpl<$Res, MealComment>; - @useResult - $Res call({String comment}); -} - -/// @nodoc -class _$MealCommentCopyWithImpl<$Res, $Val extends MealComment> - implements $MealCommentCopyWith<$Res> { - _$MealCommentCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of MealComment - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? comment = null, - }) { - return _then(_value.copyWith( - comment: null == comment - ? _value.comment - : comment // ignore: cast_nullable_to_non_nullable - as String, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$MealCommentImplCopyWith<$Res> - implements $MealCommentCopyWith<$Res> { - factory _$$MealCommentImplCopyWith( - _$MealCommentImpl value, $Res Function(_$MealCommentImpl) then) = - __$$MealCommentImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({String comment}); -} - -/// @nodoc -class __$$MealCommentImplCopyWithImpl<$Res> - extends _$MealCommentCopyWithImpl<$Res, _$MealCommentImpl> - implements _$$MealCommentImplCopyWith<$Res> { - __$$MealCommentImplCopyWithImpl( - _$MealCommentImpl _value, $Res Function(_$MealCommentImpl) _then) - : super(_value, _then); - - /// Create a copy of MealComment - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? comment = null, - }) { - return _then(_$MealCommentImpl( - comment: null == comment - ? _value.comment - : comment // ignore: cast_nullable_to_non_nullable - as String, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$MealCommentImpl implements _MealComment { - _$MealCommentImpl({required this.comment}); - - factory _$MealCommentImpl.fromJson(Map json) => - _$$MealCommentImplFromJson(json); - - @override - final String comment; - - @override - String toString() { - return 'MealComment(comment: $comment)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$MealCommentImpl && - (identical(other.comment, comment) || other.comment == comment)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, comment); - - /// Create a copy of MealComment - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$MealCommentImplCopyWith<_$MealCommentImpl> get copyWith => - __$$MealCommentImplCopyWithImpl<_$MealCommentImpl>(this, _$identity); - - @override - Map toJson() { - return _$$MealCommentImplToJson( - this, - ); - } -} - -abstract class _MealComment implements MealComment { - factory _MealComment({required final String comment}) = _$MealCommentImpl; - - factory _MealComment.fromJson(Map json) = - _$MealCommentImpl.fromJson; - - @override - String get comment; - - /// Create a copy of MealComment - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$MealCommentImplCopyWith<_$MealCommentImpl> get copyWith => - throw _privateConstructorUsedError; -} - -PortionComment _$PortionCommentFromJson(Map json) { - return _PortionComment.fromJson(json); -} - -/// @nodoc -mixin _$PortionComment { - @JsonKey(name: 'commentable_id') - int get portionId => throw _privateConstructorUsedError; - String get comment => throw _privateConstructorUsedError; - - /// Serializes this PortionComment to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of PortionComment - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $PortionCommentCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $PortionCommentCopyWith<$Res> { - factory $PortionCommentCopyWith( - PortionComment value, $Res Function(PortionComment) then) = - _$PortionCommentCopyWithImpl<$Res, PortionComment>; - @useResult - $Res call({@JsonKey(name: 'commentable_id') int portionId, String comment}); -} - -/// @nodoc -class _$PortionCommentCopyWithImpl<$Res, $Val extends PortionComment> - implements $PortionCommentCopyWith<$Res> { - _$PortionCommentCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of PortionComment - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? portionId = null, - Object? comment = null, - }) { - return _then(_value.copyWith( - portionId: null == portionId - ? _value.portionId - : portionId // ignore: cast_nullable_to_non_nullable - as int, - comment: null == comment - ? _value.comment - : comment // ignore: cast_nullable_to_non_nullable - as String, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$PortionCommentImplCopyWith<$Res> - implements $PortionCommentCopyWith<$Res> { - factory _$$PortionCommentImplCopyWith(_$PortionCommentImpl value, - $Res Function(_$PortionCommentImpl) then) = - __$$PortionCommentImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({@JsonKey(name: 'commentable_id') int portionId, String comment}); -} - -/// @nodoc -class __$$PortionCommentImplCopyWithImpl<$Res> - extends _$PortionCommentCopyWithImpl<$Res, _$PortionCommentImpl> - implements _$$PortionCommentImplCopyWith<$Res> { - __$$PortionCommentImplCopyWithImpl( - _$PortionCommentImpl _value, $Res Function(_$PortionCommentImpl) _then) - : super(_value, _then); - - /// Create a copy of PortionComment - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? portionId = null, - Object? comment = null, - }) { - return _then(_$PortionCommentImpl( - portionId: null == portionId - ? _value.portionId - : portionId // ignore: cast_nullable_to_non_nullable - as int, - comment: null == comment - ? _value.comment - : comment // ignore: cast_nullable_to_non_nullable - as String, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$PortionCommentImpl implements _PortionComment { - _$PortionCommentImpl( - {@JsonKey(name: 'commentable_id') required this.portionId, - required this.comment}); - - factory _$PortionCommentImpl.fromJson(Map json) => - _$$PortionCommentImplFromJson(json); - - @override - @JsonKey(name: 'commentable_id') - final int portionId; - @override - final String comment; - - @override - String toString() { - return 'PortionComment(portionId: $portionId, comment: $comment)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$PortionCommentImpl && - (identical(other.portionId, portionId) || - other.portionId == portionId) && - (identical(other.comment, comment) || other.comment == comment)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, portionId, comment); - - /// Create a copy of PortionComment - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$PortionCommentImplCopyWith<_$PortionCommentImpl> get copyWith => - __$$PortionCommentImplCopyWithImpl<_$PortionCommentImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$PortionCommentImplToJson( - this, - ); - } -} - -abstract class _PortionComment implements PortionComment { - factory _PortionComment( - {@JsonKey(name: 'commentable_id') required final int portionId, - required final String comment}) = _$PortionCommentImpl; - - factory _PortionComment.fromJson(Map json) = - _$PortionCommentImpl.fromJson; - - @override - @JsonKey(name: 'commentable_id') - int get portionId; - @override - String get comment; - - /// Create a copy of PortionComment - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$PortionCommentImplCopyWith<_$PortionCommentImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/features/diary/models/consultant_review_model.g.dart b/lib/features/diary/models/consultant_review_model.g.dart deleted file mode 100644 index f08faeb1..00000000 --- a/lib/features/diary/models/consultant_review_model.g.dart +++ /dev/null @@ -1,69 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'consultant_review_model.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$ConsultantReviewModelImpl _$$ConsultantReviewModelImplFromJson( - Map json) => - _$ConsultantReviewModelImpl( - planName: json['planName'] as String, - planDuration: (json['planDuration'] as num).toInt(), - currentPlanDay: (json['currentPlanDay'] as num).toInt(), - consultantComments: ConsultantComments.fromJson( - json['consultantComments'] as Map), - ); - -Map _$$ConsultantReviewModelImplToJson( - _$ConsultantReviewModelImpl instance) => - { - 'planName': instance.planName, - 'planDuration': instance.planDuration, - 'currentPlanDay': instance.currentPlanDay, - 'consultantComments': instance.consultantComments, - }; - -_$ConsultantCommentsImpl _$$ConsultantCommentsImplFromJson( - Map json) => - _$ConsultantCommentsImpl( - dayComment: json['dayComment'] as String?, - mealsComments: (json['mealsComments'] as List) - .map((e) => MealComment.fromJson(e as Map)) - .toList(), - portionsComments: (json['portionsComments'] as List) - .map((e) => PortionComment.fromJson(e as Map)) - .toList(), - ); - -Map _$$ConsultantCommentsImplToJson( - _$ConsultantCommentsImpl instance) => - { - 'dayComment': instance.dayComment, - 'mealsComments': instance.mealsComments, - 'portionsComments': instance.portionsComments, - }; - -_$MealCommentImpl _$$MealCommentImplFromJson(Map json) => - _$MealCommentImpl( - comment: json['comment'] as String, - ); - -Map _$$MealCommentImplToJson(_$MealCommentImpl instance) => - { - 'comment': instance.comment, - }; - -_$PortionCommentImpl _$$PortionCommentImplFromJson(Map json) => - _$PortionCommentImpl( - portionId: (json['commentable_id'] as num).toInt(), - comment: json['comment'] as String, - ); - -Map _$$PortionCommentImplToJson( - _$PortionCommentImpl instance) => - { - 'commentable_id': instance.portionId, - 'comment': instance.comment, - }; diff --git a/lib/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_state.freezed.dart b/lib/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_state.freezed.dart deleted file mode 100644 index 2f527110..00000000 --- a/lib/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_state.freezed.dart +++ /dev/null @@ -1,190 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'consultant_comments_state.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -/// @nodoc -mixin _$ConsultantCommentsState { - ConsultantCommentModel get commentsModel => - throw _privateConstructorUsedError; - bool get dayCommentNeedsToBeFilled => throw _privateConstructorUsedError; - - /// Create a copy of ConsultantCommentsState - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $ConsultantCommentsStateCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $ConsultantCommentsStateCopyWith<$Res> { - factory $ConsultantCommentsStateCopyWith(ConsultantCommentsState value, - $Res Function(ConsultantCommentsState) then) = - _$ConsultantCommentsStateCopyWithImpl<$Res, ConsultantCommentsState>; - @useResult - $Res call( - {ConsultantCommentModel commentsModel, bool dayCommentNeedsToBeFilled}); - - $ConsultantCommentModelCopyWith<$Res> get commentsModel; -} - -/// @nodoc -class _$ConsultantCommentsStateCopyWithImpl<$Res, - $Val extends ConsultantCommentsState> - implements $ConsultantCommentsStateCopyWith<$Res> { - _$ConsultantCommentsStateCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of ConsultantCommentsState - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? commentsModel = null, - Object? dayCommentNeedsToBeFilled = null, - }) { - return _then(_value.copyWith( - commentsModel: null == commentsModel - ? _value.commentsModel - : commentsModel // ignore: cast_nullable_to_non_nullable - as ConsultantCommentModel, - dayCommentNeedsToBeFilled: null == dayCommentNeedsToBeFilled - ? _value.dayCommentNeedsToBeFilled - : dayCommentNeedsToBeFilled // ignore: cast_nullable_to_non_nullable - as bool, - ) as $Val); - } - - /// Create a copy of ConsultantCommentsState - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $ConsultantCommentModelCopyWith<$Res> get commentsModel { - return $ConsultantCommentModelCopyWith<$Res>(_value.commentsModel, (value) { - return _then(_value.copyWith(commentsModel: value) as $Val); - }); - } -} - -/// @nodoc -abstract class _$$ConsultantCommentsStateImplCopyWith<$Res> - implements $ConsultantCommentsStateCopyWith<$Res> { - factory _$$ConsultantCommentsStateImplCopyWith( - _$ConsultantCommentsStateImpl value, - $Res Function(_$ConsultantCommentsStateImpl) then) = - __$$ConsultantCommentsStateImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {ConsultantCommentModel commentsModel, bool dayCommentNeedsToBeFilled}); - - @override - $ConsultantCommentModelCopyWith<$Res> get commentsModel; -} - -/// @nodoc -class __$$ConsultantCommentsStateImplCopyWithImpl<$Res> - extends _$ConsultantCommentsStateCopyWithImpl<$Res, - _$ConsultantCommentsStateImpl> - implements _$$ConsultantCommentsStateImplCopyWith<$Res> { - __$$ConsultantCommentsStateImplCopyWithImpl( - _$ConsultantCommentsStateImpl _value, - $Res Function(_$ConsultantCommentsStateImpl) _then) - : super(_value, _then); - - /// Create a copy of ConsultantCommentsState - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? commentsModel = null, - Object? dayCommentNeedsToBeFilled = null, - }) { - return _then(_$ConsultantCommentsStateImpl( - commentsModel: null == commentsModel - ? _value.commentsModel - : commentsModel // ignore: cast_nullable_to_non_nullable - as ConsultantCommentModel, - dayCommentNeedsToBeFilled: null == dayCommentNeedsToBeFilled - ? _value.dayCommentNeedsToBeFilled - : dayCommentNeedsToBeFilled // ignore: cast_nullable_to_non_nullable - as bool, - )); - } -} - -/// @nodoc - -class _$ConsultantCommentsStateImpl implements _ConsultantCommentsState { - _$ConsultantCommentsStateImpl( - {required this.commentsModel, this.dayCommentNeedsToBeFilled = false}); - - @override - final ConsultantCommentModel commentsModel; - @override - @JsonKey() - final bool dayCommentNeedsToBeFilled; - - @override - String toString() { - return 'ConsultantCommentsState(commentsModel: $commentsModel, dayCommentNeedsToBeFilled: $dayCommentNeedsToBeFilled)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$ConsultantCommentsStateImpl && - (identical(other.commentsModel, commentsModel) || - other.commentsModel == commentsModel) && - (identical(other.dayCommentNeedsToBeFilled, - dayCommentNeedsToBeFilled) || - other.dayCommentNeedsToBeFilled == dayCommentNeedsToBeFilled)); - } - - @override - int get hashCode => - Object.hash(runtimeType, commentsModel, dayCommentNeedsToBeFilled); - - /// Create a copy of ConsultantCommentsState - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$ConsultantCommentsStateImplCopyWith<_$ConsultantCommentsStateImpl> - get copyWith => __$$ConsultantCommentsStateImplCopyWithImpl< - _$ConsultantCommentsStateImpl>(this, _$identity); -} - -abstract class _ConsultantCommentsState implements ConsultantCommentsState { - factory _ConsultantCommentsState( - {required final ConsultantCommentModel commentsModel, - final bool dayCommentNeedsToBeFilled}) = _$ConsultantCommentsStateImpl; - - @override - ConsultantCommentModel get commentsModel; - @override - bool get dayCommentNeedsToBeFilled; - - /// Create a copy of ConsultantCommentsState - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$ConsultantCommentsStateImplCopyWith<_$ConsultantCommentsStateImpl> - get copyWith => throw _privateConstructorUsedError; -} diff --git a/lib/features/notifications/models/notification_model.freezed.dart b/lib/features/notifications/models/notification_model.freezed.dart deleted file mode 100644 index 2ea9b158..00000000 --- a/lib/features/notifications/models/notification_model.freezed.dart +++ /dev/null @@ -1,166 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'notification_model.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -NotificationModel _$NotificationModelFromJson(Map json) { - return _NotificationModel.fromJson(json); -} - -/// @nodoc -mixin _$NotificationModel { - NotificationType get type => throw _privateConstructorUsedError; - - /// Serializes this NotificationModel to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of NotificationModel - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $NotificationModelCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $NotificationModelCopyWith<$Res> { - factory $NotificationModelCopyWith( - NotificationModel value, $Res Function(NotificationModel) then) = - _$NotificationModelCopyWithImpl<$Res, NotificationModel>; - @useResult - $Res call({NotificationType type}); -} - -/// @nodoc -class _$NotificationModelCopyWithImpl<$Res, $Val extends NotificationModel> - implements $NotificationModelCopyWith<$Res> { - _$NotificationModelCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of NotificationModel - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? type = null, - }) { - return _then(_value.copyWith( - type: null == type - ? _value.type - : type // ignore: cast_nullable_to_non_nullable - as NotificationType, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$NotificationModelImplCopyWith<$Res> - implements $NotificationModelCopyWith<$Res> { - factory _$$NotificationModelImplCopyWith(_$NotificationModelImpl value, - $Res Function(_$NotificationModelImpl) then) = - __$$NotificationModelImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({NotificationType type}); -} - -/// @nodoc -class __$$NotificationModelImplCopyWithImpl<$Res> - extends _$NotificationModelCopyWithImpl<$Res, _$NotificationModelImpl> - implements _$$NotificationModelImplCopyWith<$Res> { - __$$NotificationModelImplCopyWithImpl(_$NotificationModelImpl _value, - $Res Function(_$NotificationModelImpl) _then) - : super(_value, _then); - - /// Create a copy of NotificationModel - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? type = null, - }) { - return _then(_$NotificationModelImpl( - type: null == type - ? _value.type - : type // ignore: cast_nullable_to_non_nullable - as NotificationType, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$NotificationModelImpl implements _NotificationModel { - _$NotificationModelImpl({required this.type}); - - factory _$NotificationModelImpl.fromJson(Map json) => - _$$NotificationModelImplFromJson(json); - - @override - final NotificationType type; - - @override - String toString() { - return 'NotificationModel(type: $type)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$NotificationModelImpl && - (identical(other.type, type) || other.type == type)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, type); - - /// Create a copy of NotificationModel - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$NotificationModelImplCopyWith<_$NotificationModelImpl> get copyWith => - __$$NotificationModelImplCopyWithImpl<_$NotificationModelImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$NotificationModelImplToJson( - this, - ); - } -} - -abstract class _NotificationModel implements NotificationModel { - factory _NotificationModel({required final NotificationType type}) = - _$NotificationModelImpl; - - factory _NotificationModel.fromJson(Map json) = - _$NotificationModelImpl.fromJson; - - @override - NotificationType get type; - - /// Create a copy of NotificationModel - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$NotificationModelImplCopyWith<_$NotificationModelImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/features/notifications/models/notification_model.g.dart b/lib/features/notifications/models/notification_model.g.dart deleted file mode 100644 index 9d68bb8d..00000000 --- a/lib/features/notifications/models/notification_model.g.dart +++ /dev/null @@ -1,28 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'notification_model.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$NotificationModelImpl _$$NotificationModelImplFromJson( - Map json) => - _$NotificationModelImpl( - type: $enumDecode(_$NotificationTypeEnumMap, json['type']), - ); - -Map _$$NotificationModelImplToJson( - _$NotificationModelImpl instance) => - { - 'type': _$NotificationTypeEnumMap[instance.type]!, - }; - -const _$NotificationTypeEnumMap = { - NotificationType.userSubscribedNotification: - 'App\\Notifications\\UserSubscribedNotification', - NotificationType.planSubscriptionStartedToAuthor: - 'App\\Notifications\\Plan\\PlanSubscriptionStartedToAuthor', - NotificationType.planSubscriptionCanceledToAuthor: - 'App\\Notifications\\Plan\\PlanSubscriptionCanceledToAuthor', -}; From 27b165669063fde2da615672cb4546c72e04c880 Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Fri, 25 Apr 2025 12:56:23 +0300 Subject: [PATCH 03/26] feat(avatar): task 394 placeholder empty avatar --- .../presentation/pages/blog_post_screen.dart | 14 +++++++++++--- .../widgets/bottom_navigation_bar_widget.dart | 3 ++- .../chat/presentation/pages/chat_screen.dart | 1 + .../presentation/widgets/chat_entry_widget.dart | 3 ++- .../presentation/widgets/blog_post_widget.dart | 1 + .../feed/presentation/widgets/comments_widget.dart | 1 + .../presentation/widgets/consultant_widget.dart | 1 + .../profile/presentation/pages/profile_screen.dart | 3 ++- .../widgets/profile_blog_post_widget.dart | 1 + .../profile/presentation/widgets/sub_widget.dart | 3 ++- 10 files changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/features/blog_post/presentation/pages/blog_post_screen.dart b/lib/features/blog_post/presentation/pages/blog_post_screen.dart index 4f6a0ad0..63f87233 100644 --- a/lib/features/blog_post/presentation/pages/blog_post_screen.dart +++ b/lib/features/blog_post/presentation/pages/blog_post_screen.dart @@ -104,8 +104,10 @@ class _BlogPostScreenState extends State { height: 1.2, ), decoration: InputDecoration( - isDense: false, // убираем true, чтобы padding учитывался корректно - contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), // вертикальный паддинг увеличен + isDense: false, + // убираем true, чтобы padding учитывался корректно + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), + // вертикальный паддинг увеличен hintText: 'Комментарий', hintStyle: const TextStyle( fontSize: 16, @@ -209,7 +211,13 @@ class _BlogPostScreenState extends State { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.start, children: [ - CircleAvatar(radius: 16, backgroundImage: NetworkImage(widget.blogPost.author.authorProfileIcon)), + CircleAvatar( + radius: 16, + foregroundImage: NetworkImage( + widget.blogPost.author.authorProfileIcon, + ), + backgroundImage: const AssetImage('assets/images/no_avatar.png'), + ), const SizedBox(width: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart b/lib/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart index 5fdf10c0..c0b5e16a 100644 --- a/lib/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart +++ b/lib/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart @@ -81,7 +81,8 @@ class _BottomNavigationBarWidgetState extends State { BottomNavigationBarItem( label: '', icon: CircleAvatar( - backgroundImage: NetworkImage(widget.profileModel.profilePhotoUrl), + foregroundImage: NetworkImage(widget.profileModel.profilePhotoUrl), + backgroundImage: const AssetImage('assets/images/no_avatar.png'), radius: 16, ), ), diff --git a/lib/features/chat/presentation/pages/chat_screen.dart b/lib/features/chat/presentation/pages/chat_screen.dart index 5f14e0bf..6cf7c9da 100644 --- a/lib/features/chat/presentation/pages/chat_screen.dart +++ b/lib/features/chat/presentation/pages/chat_screen.dart @@ -128,6 +128,7 @@ class _ChatScreenState extends State { CircleAvatar( radius: 20, foregroundImage: NetworkImage(state.chatRoom.users.receiver.profilePhotoUrl!), + backgroundImage: const AssetImage('assets/images/no_avatar.png'), ), if (state.chatRoom.users.receiver.profilePhotoUrl == null) Container( diff --git a/lib/features/chat/presentation/widgets/chat_entry_widget.dart b/lib/features/chat/presentation/widgets/chat_entry_widget.dart index bde4f8fd..1338c60b 100644 --- a/lib/features/chat/presentation/widgets/chat_entry_widget.dart +++ b/lib/features/chat/presentation/widgets/chat_entry_widget.dart @@ -40,9 +40,10 @@ class _ChatEntryWidgetState extends State { children: [ CircleAvatar( radius: 24, - backgroundImage: NetworkImage( + foregroundImage: NetworkImage( widget.chatRoom.users.toChatUser.profilePhotoUrl, ), + backgroundImage: const AssetImage('assets/images/no_avatar.png'), backgroundColor: Colors.white, ), const SizedBox(width: 12), diff --git a/lib/features/feed/presentation/widgets/blog_post_widget.dart b/lib/features/feed/presentation/widgets/blog_post_widget.dart index 819d8c62..605df679 100644 --- a/lib/features/feed/presentation/widgets/blog_post_widget.dart +++ b/lib/features/feed/presentation/widgets/blog_post_widget.dart @@ -81,6 +81,7 @@ class _BlogPostWidgetState extends State { foregroundImage: NetworkImage( state.feedBlogPostList[widget.index].author.authorProfileIcon, ), + backgroundImage: const AssetImage('assets/images/no_avatar.png'), ), const SizedBox(width: 8), Column( diff --git a/lib/features/feed/presentation/widgets/comments_widget.dart b/lib/features/feed/presentation/widgets/comments_widget.dart index 784a77c5..338e4f5a 100644 --- a/lib/features/feed/presentation/widgets/comments_widget.dart +++ b/lib/features/feed/presentation/widgets/comments_widget.dart @@ -61,6 +61,7 @@ class _CommentsWidgetState extends State { children: [ CircleAvatar( foregroundImage: NetworkImage(comment.authorProfileIcon), + backgroundImage: const AssetImage('assets/images/no_avatar.png'), radius: radius, ), const SizedBox(width: 8), diff --git a/lib/features/feed/presentation/widgets/consultant_widget.dart b/lib/features/feed/presentation/widgets/consultant_widget.dart index c9d26e47..60b84dc9 100644 --- a/lib/features/feed/presentation/widgets/consultant_widget.dart +++ b/lib/features/feed/presentation/widgets/consultant_widget.dart @@ -64,6 +64,7 @@ class ConsultantWidget extends StatelessWidget { const SizedBox(width: 8), CircleAvatar( foregroundImage: NetworkImage(consultant.photo), + backgroundImage: const AssetImage('assets/images/no_avatar.png'), ), const SizedBox(width: 8), Column( diff --git a/lib/features/profile/presentation/pages/profile_screen.dart b/lib/features/profile/presentation/pages/profile_screen.dart index 12e4c4ea..74c2903a 100644 --- a/lib/features/profile/presentation/pages/profile_screen.dart +++ b/lib/features/profile/presentation/pages/profile_screen.dart @@ -799,9 +799,10 @@ class _ProfileScreenState extends State { widthFactor: 0.8, child: CircleAvatar( radius: 14, - backgroundImage: NetworkImage( + foregroundImage: NetworkImage( urls[i], ), + backgroundImage: const AssetImage('assets/images/no_avatar.png'), ), ), ], diff --git a/lib/features/profile/presentation/widgets/profile_blog_post_widget.dart b/lib/features/profile/presentation/widgets/profile_blog_post_widget.dart index 06bcc85d..ab920ea2 100644 --- a/lib/features/profile/presentation/widgets/profile_blog_post_widget.dart +++ b/lib/features/profile/presentation/widgets/profile_blog_post_widget.dart @@ -68,6 +68,7 @@ class _ProfileBlogPostWidgetState extends State { foregroundImage: NetworkImage( state.blogPosts[widget.index].author.authorProfileIcon, ), + backgroundImage: const AssetImage('assets/images/no_avatar.png'), ), const SizedBox(width: 8), Column( diff --git a/lib/features/profile/presentation/widgets/sub_widget.dart b/lib/features/profile/presentation/widgets/sub_widget.dart index 429772d3..c8342077 100644 --- a/lib/features/profile/presentation/widgets/sub_widget.dart +++ b/lib/features/profile/presentation/widgets/sub_widget.dart @@ -35,7 +35,8 @@ class _SubWidgetState extends State { child: Row( children: [ CircleAvatar( - backgroundImage: NetworkImage(widget.subModel.profilePhotoUrl), + foregroundImage: NetworkImage(widget.subModel.profilePhotoUrl), + backgroundImage: const AssetImage('assets/images/no_avatar.png'), radius: 28, ), const SizedBox(width: 12), From 886dfa2570949df9612c7c1dc14f44e262482b1d Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Fri, 25 Apr 2025 13:17:37 +0300 Subject: [PATCH 04/26] fix(finance): task 332 remove card manage for user and red color for canceled --- .../entities/consultant_finance_entity.dart | 1 + .../pages/user_finance_screen.dart | 316 ++++++++++-------- 2 files changed, 169 insertions(+), 148 deletions(-) diff --git a/lib/features/my_finance/entities/consultant_finance_entity.dart b/lib/features/my_finance/entities/consultant_finance_entity.dart index 7bcf86ef..77568937 100644 --- a/lib/features/my_finance/entities/consultant_finance_entity.dart +++ b/lib/features/my_finance/entities/consultant_finance_entity.dart @@ -1,6 +1,7 @@ enum ConsultationStatus { completed, canceled, + inProgress } class ConsultantFinanceEntity { diff --git a/lib/features/my_finance/presentation/pages/user_finance_screen.dart b/lib/features/my_finance/presentation/pages/user_finance_screen.dart index 3eecb395..d9f56ef6 100644 --- a/lib/features/my_finance/presentation/pages/user_finance_screen.dart +++ b/lib/features/my_finance/presentation/pages/user_finance_screen.dart @@ -3,9 +3,12 @@ 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/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart'; +import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_state.dart'; import 'package:eatvio_app/features/my_finance/entities/consultant_finance_entity.dart'; import 'package:eatvio_app/features/my_finance/presentation/pages/card_settings.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; class UserFinanceScreen extends StatefulWidget { @@ -20,162 +23,179 @@ class _UserFinanceScreenState extends State { @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: [ - const SizedBox(height: 8), - InkWell( - onTap: () { - GoRouter.of(context).pushNamed(CardSettingsScreen.name); - }, - borderRadius: BorderRadius.circular(20), - child: Ink( - decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), color: appTheme.blueA400), - width: MediaQuery.of(context).size.width - 32, - child: const Center( - child: Padding( - padding: EdgeInsets.all(16), - child: Text('Управление картами', style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600)), - ), - ), - ), + return BlocBuilder( + builder: (BuildContext context, ProfileAvatarState state) { + if (state is ProfileAvatarStateLoaded) { + return Scaffold( + backgroundColor: const Color(0xFFF0F2F5), + appBar: AppBar( + shape: const RoundedRectangleBorder(borderRadius: BorderRadiusDirectional.vertical(bottom: Radius.circular(12))), + title: const Text('Финансы'), + centerTitle: true, ), - const SizedBox(height: 16), - 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), - ListView.separated( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, index) => Material( - color: const Color(0xFFF6F6F6), - borderRadius: BorderRadius.circular(20), + body: SingleChildScrollView( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Column( + children: [ + if (state.profile.roles.contains('consultant')) ...[ + const SizedBox(height: 8), + InkWell( + onTap: () { + GoRouter.of(context).pushNamed(CardSettingsScreen.name); + }, + borderRadius: BorderRadius.circular(20), + child: Ink( + decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), color: appTheme.blueA400), + width: MediaQuery.of(context).size.width - 32, + child: const Center( child: Padding( - padding: const EdgeInsets.all(12), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Tooltip( - message: testFinanceEntity.consultationsList[index].id, - triggerMode: TooltipTriggerMode.tap, - textStyle: const TextStyle( - color: Colors.black, - fontSize: 16, - fontWeight: FontWeight.w500, - ), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(4), - boxShadow: [ - BoxShadow( - color: Colors.black.withAlpha((255 * 0.2).toInt()), - blurRadius: 32, - ) - ], - ), - child: 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), - ), - ), - ), - ], - ), - ], - ), + padding: EdgeInsets.all(16), + child: Text('Управление картами', style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600)), ), ), - separatorBuilder: (context, index) => const SizedBox(height: 12), - itemCount: testFinanceEntity.consultationsList.length, ), - ], + ), + const SizedBox(height: 16), + ], + 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), + 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: [ + Tooltip( + message: testFinanceEntity.consultationsList[index].id, + triggerMode: TooltipTriggerMode.tap, + textStyle: const TextStyle( + color: Colors.black, + fontSize: 16, + fontWeight: FontWeight.w500, + ), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(4), + boxShadow: [ + BoxShadow( + color: Colors.black.withAlpha((255 * 0.2).toInt()), + blurRadius: 32, + ) + ], + ), + child: 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: testFinanceEntity.consultationsList[index].status == ConsultationStatus.canceled + ? const Color(0xFDD01717).withAlpha((255 * 0.1).toInt()) + : 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: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: testFinanceEntity.consultationsList[index].status == ConsultationStatus.canceled + ? const Color(0xFDD01717) + : const Color(0xFF0BB450), + height: 1), + ), + ), + ), + ], + ), + ], + ), + ), + ), + separatorBuilder: (context, index) => const SizedBox(height: 12), + itemCount: testFinanceEntity.consultationsList.length, + ), + ], + ), + ), + ), ), - ), + ], ), ), - ], - ), - ), + ); + } + return Container(); + }, ); } } From a52a5383debd0ccf15e424f150416c14a8586ff7 Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Fri, 25 Apr 2025 13:50:23 +0300 Subject: [PATCH 05/26] fix(feed): task 374 raing score, description --- .../feed/presentation/widgets/consultant_widget.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/features/feed/presentation/widgets/consultant_widget.dart b/lib/features/feed/presentation/widgets/consultant_widget.dart index 60b84dc9..3964eb06 100644 --- a/lib/features/feed/presentation/widgets/consultant_widget.dart +++ b/lib/features/feed/presentation/widgets/consultant_widget.dart @@ -81,7 +81,7 @@ class ConsultantWidget extends StatelessWidget { ), const SizedBox(height: 4), Text( - 'Рейтинг: RATING', + 'Рейтинг: ${consultant.totalScores}', style: TextStyle( color: appTheme.blueA400, height: 1, @@ -106,7 +106,11 @@ class ConsultantWidget extends StatelessWidget { ], ), const SizedBox(height: 8), - const Text('description'), + Text( + consultant.description, + style: TextStyle(), + maxLines: 2, + ), if (consultant.badges.isNotEmpty) const SizedBox(height: 8), _badges(consultant.badges, context), ], From c8b828bfe19a76110e72af47a1b438b5561337bc Mon Sep 17 00:00:00 2001 From: Username1548 Date: Fri, 25 Apr 2025 19:10:08 +0700 Subject: [PATCH 06/26] fix(profile): profile subs fixed --- lib/features/profile/models/profile_model.dart | 4 ++-- lib/features/profile/presentation/pages/profile_screen.dart | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/features/profile/models/profile_model.dart b/lib/features/profile/models/profile_model.dart index 9d4e877f..6aeb1767 100644 --- a/lib/features/profile/models/profile_model.dart +++ b/lib/features/profile/models/profile_model.dart @@ -21,8 +21,8 @@ class ProfileModel with _$ProfileModel { @JsonKey(name: "profile_photo_url") required String profilePhotoUrl, @JsonKey(name: "roles") required List roles, @JsonKey(name: "badges") required List badges, - @JsonKey(name: "subscriptions") required List subscriptions, - @JsonKey(name: "subscribers") required List subscribers, + @JsonKey(name: "subscriptions") required List subscriptions, + @JsonKey(name: "subscribers") required List subscribers, @JsonKey(name: "education_resource") EducationModel? educationResource, @JsonKey(name: "cert_photos") List? diplomasAndCertificates, }) = _ProfileModel; diff --git a/lib/features/profile/presentation/pages/profile_screen.dart b/lib/features/profile/presentation/pages/profile_screen.dart index 12e4c4ea..2acd9bac 100644 --- a/lib/features/profile/presentation/pages/profile_screen.dart +++ b/lib/features/profile/presentation/pages/profile_screen.dart @@ -728,7 +728,7 @@ class _ProfileScreenState extends State { ), Padding( padding: const EdgeInsets.only(right: 8), - child: _miniAvatars(state.profile.subscriptions.map((e) => e.profilePhotoUrl).toList()), + child: _miniAvatars(state.profile.subscriptions), ), ], ), @@ -778,7 +778,7 @@ class _ProfileScreenState extends State { ), Padding( padding: const EdgeInsets.only(right: 8), - child: _miniAvatars(state.profile.subscribers.map((e) => e.profilePhotoUrl).toList()), + child: _miniAvatars(state.profile.subscribers), ), ], ), From 746aab2fae94999a6909ba5230a8f139465d785a Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Fri, 25 Apr 2025 15:25:48 +0300 Subject: [PATCH 07/26] feat(profile): 371 task badge description onTap --- lib/features/profile/models/sub_model.dart | 6 +-- .../presentation/pages/profile_screen.dart | 54 ++++++++++++++++--- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/lib/features/profile/models/sub_model.dart b/lib/features/profile/models/sub_model.dart index 4623e198..c4a04bd7 100644 --- a/lib/features/profile/models/sub_model.dart +++ b/lib/features/profile/models/sub_model.dart @@ -19,18 +19,14 @@ class SubPaginationModel with _$SubPaginationModel { @freezed class SubModel with _$SubModel { const factory SubModel({ - required int id, required String slug, required String name, required String city, required DateTime birthdate, @JsonKey(name: 'is_entity_subscribed_by_me') required bool isEntitySubscribedByMe, - required String email, @JsonKey(name: 'profile_photo_url') required String profilePhotoUrl, - required String role, + required List roles, @JsonKey(name: 'is_public_profile') required bool isPublicProfile, - @JsonKey(name: 'created_at') required DateTime createdAt, - @JsonKey(name: 'updated_at') required DateTime updatedAt, }) = _SubModel; factory SubModel.fromJson(Map json) => _$SubModelFromJson(json); diff --git a/lib/features/profile/presentation/pages/profile_screen.dart b/lib/features/profile/presentation/pages/profile_screen.dart index 5933a3ed..e127d68c 100644 --- a/lib/features/profile/presentation/pages/profile_screen.dart +++ b/lib/features/profile/presentation/pages/profile_screen.dart @@ -658,15 +658,53 @@ class _ProfileScreenState extends State { children: [ for (int i = 0; i < state.profile.badges.length; i++) ...[ state.profile.badges[i].image.contains('svg') - ? SvgPicture.network( - '$baseUrl/storage/${state.profile.badges[i].image}', - width: 40, - height: 40, + ? Tooltip( + message: state.profile.badges[i].description, + triggerMode: TooltipTriggerMode.tap, + textStyle: const TextStyle( + color: Colors.black, + fontSize: 16, + fontWeight: FontWeight.w500, + ), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(4), + boxShadow: [ + BoxShadow( + color: Colors.black.withAlpha((255 * 0.2).toInt()), + blurRadius: 32, + ) + ], + ), + child: SvgPicture.network( + '$baseUrl/storage/${state.profile.badges[i].image}', + width: 40, + height: 40, + ), ) - : CachedNetworkImage( - imageUrl: '$baseUrl/storage/${state.profile.badges[i].image}', - width: 40, - height: 40, + : Tooltip( + message: state.profile.badges[i].description, + triggerMode: TooltipTriggerMode.tap, + textStyle: const TextStyle( + color: Colors.black, + fontSize: 16, + fontWeight: FontWeight.w500, + ), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(4), + boxShadow: [ + BoxShadow( + color: Colors.black.withAlpha((255 * 0.2).toInt()), + blurRadius: 32, + ) + ], + ), + child: CachedNetworkImage( + imageUrl: '$baseUrl/storage/${state.profile.badges[i].image}', + width: 40, + height: 40, + ), ), i < state.profile.badges.length - 1 ? const SizedBox(width: 8) : const SizedBox.shrink(), ], From 4aad916c1e119e37fb21532459c5735ff9a9343b Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Fri, 25 Apr 2025 15:33:10 +0300 Subject: [PATCH 08/26] feat(support): task 397 email copy --- .../presentation/pages/support_screen.dart | 15 +++++++++------ lib/generated/intl/messages_en.dart | 3 +++ lib/generated/intl/messages_ru.dart | 3 +++ lib/generated/l10n.dart | 10 ++++++++++ lib/l10n/intl_en.arb | 1 + lib/l10n/intl_ru.arb | 1 + 6 files changed, 27 insertions(+), 6 deletions(-) diff --git a/lib/features/support/presentation/pages/support_screen.dart b/lib/features/support/presentation/pages/support_screen.dart index a5a298e2..5a80e62b 100644 --- a/lib/features/support/presentation/pages/support_screen.dart +++ b/lib/features/support/presentation/pages/support_screen.dart @@ -1,4 +1,7 @@ +import 'package:eatvio_app/core/utils/toasts.dart'; +import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -7,6 +10,8 @@ class SupportScreen extends StatelessWidget { @override Widget build(BuildContext context) { + final s = S.of(context); + return Scaffold( appBar: AppBar( title: const Text('Поддержка'), @@ -44,7 +49,7 @@ class SupportScreen extends StatelessWidget { ), const SizedBox(width: 12), const Text( - 'Telegram', + 'Telegram: t.me/eatvio', style: TextStyle( fontSize: 16, ), @@ -56,10 +61,8 @@ class SupportScreen extends StatelessWidget { const SizedBox(height: 8), InkWell( onTap: () { - launchUrl( - Uri.parse('mailto:help@eatv.io'), - mode: LaunchMode.externalApplication, - ); + Clipboard.setData(const ClipboardData(text: 'help@eatv.io')); + showInfoToast(s.mail_was_copied); }, borderRadius: BorderRadius.circular(16), child: Ink( @@ -73,7 +76,7 @@ class SupportScreen extends StatelessWidget { Icon(Icons.alternate_email_rounded), SizedBox(width: 12), Text( - 'E-mail', + 'E-mail: help@eatv.io', style: TextStyle( fontSize: 16, ), diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 96602566..0bea54af 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -371,6 +371,9 @@ class MessageLookup extends MessageLookupByLibrary { ), "loading": MessageLookupByLibrary.simpleMessage("Загрузка..."), "lower": MessageLookupByLibrary.simpleMessage("Нижнее"), + "mail_was_copied": MessageLookupByLibrary.simpleMessage( + "Почта успешно скопирована", + ), "male": MessageLookupByLibrary.simpleMessage("Мужской"), "meal": MessageLookupByLibrary.simpleMessage("Приём пищи"), "mealComment": MessageLookupByLibrary.simpleMessage( diff --git a/lib/generated/intl/messages_ru.dart b/lib/generated/intl/messages_ru.dart index 91355290..82821c9a 100644 --- a/lib/generated/intl/messages_ru.dart +++ b/lib/generated/intl/messages_ru.dart @@ -209,6 +209,9 @@ class MessageLookup extends MessageLookupByLibrary { "Ссылка успешно скопирована", ), "loading": MessageLookupByLibrary.simpleMessage("Загрузка..."), + "mail_was_copied": MessageLookupByLibrary.simpleMessage( + "Почта успешно скопирована", + ), "male": MessageLookupByLibrary.simpleMessage("Мужской"), "meal": MessageLookupByLibrary.simpleMessage("Приём пищи"), "meat": MessageLookupByLibrary.simpleMessage("Мясо"), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 260c15f5..e65a9fa9 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -1055,6 +1055,16 @@ class S { ); } + /// `Почта успешно скопирована` + String get mail_was_copied { + return Intl.message( + 'Почта успешно скопирована', + name: 'mail_was_copied', + desc: '', + args: [], + ); + } + /// `Выберите дату старты диеты. Она отобразиться у Вас в дневнике` String get choose_start_diet_date { return Intl.message( diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index b0f45e86..c083a860 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -129,6 +129,7 @@ "carbohydrates": "Углеводы", "answer": "Ответить", "link_was_copied": "Ссылка успешно скопирована", + "mail_was_copied": "Почта успешно скопирована", "choose_start_diet_date": "Выберите дату старты диеты. Она отобразиться у Вас в дневнике", "continue_": "Продолжить", "order": "Порядок", diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index 1f1259f0..221d36ff 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -129,6 +129,7 @@ "carbohydrates": "Углеводы", "answer": "Ответить", "link_was_copied": "Ссылка успешно скопирована", + "mail_was_copied": "Почта успешно скопирована", "choose_start_diet_date": "Выберите дату старты диеты. Она отобразиться у Вас в дневнике", "continue_": "Продолжить", "order": "Порядок", From af63b82f523e80bcbeb96720c4d56fdd6ab29918 Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Fri, 25 Apr 2025 15:33:47 +0300 Subject: [PATCH 09/26] fix: analyzer --- lib/features/feed/presentation/widgets/consultant_widget.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/features/feed/presentation/widgets/consultant_widget.dart b/lib/features/feed/presentation/widgets/consultant_widget.dart index 3964eb06..061bc0db 100644 --- a/lib/features/feed/presentation/widgets/consultant_widget.dart +++ b/lib/features/feed/presentation/widgets/consultant_widget.dart @@ -108,7 +108,6 @@ class ConsultantWidget extends StatelessWidget { const SizedBox(height: 8), Text( consultant.description, - style: TextStyle(), maxLines: 2, ), if (consultant.badges.isNotEmpty) const SizedBox(height: 8), From 0fcb2c1a0d526d69f3ee9df8a85695b5e4f65c52 Mon Sep 17 00:00:00 2001 From: Username1548 Date: Tue, 29 Apr 2025 14:44:33 +0700 Subject: [PATCH 10/26] feat(plans): plan creating has been completed --- .DS_Store | Bin 10244 -> 10244 bytes assets/icons/star.svg | 3 + lib/core/app_router.dart | 8 +- lib/core/locator_service.dart | 6 +- lib/core/theme/theme_helper.dart | 2 + lib/core/widgets/custom_text_form_field.dart | 13 +- .../success_published_diet_bottom_sheet.dart | 7 +- .../bloc/chat_room_cubit/chat_room_cubit.dart | 8 +- .../chat/presentation/pages/chat_screen.dart | 31 +- .../widgets/notification_banner.dart | 219 ++++++---- .../presentation/create_diet_screen.dart | 8 +- .../create_plan/models/create_plan_model.dart | 11 +- .../create_plan/models/plan_slug_model.dart | 11 + .../presentation/create_plan_screen.dart | 391 ++++++++++-------- .../presentation/cubit/create_plan_cubit.dart | 343 ++++++++++++++- .../presentation/cubit/create_plan_state.dart | 52 ++- .../presentation/widgets/add_item_widget.dart | 75 ++-- .../presentation/widgets/option_widget.dart | 110 ++--- .../plan_duration_drop_down_widget.dart | 54 ++- .../widgets/plan_image_picker_widget.dart | 140 ++++--- .../widgets/plan_type_choice_widget.dart | 83 ++-- .../repositories/create_plan_repository.dart | 7 +- .../services/create_plan_service.dart | 7 +- .../diary/models/consultant_review_model.dart | 3 +- .../consultant_review_model.freezed.dart | 47 ++- .../models/consultant_review_model.g.dart | 6 +- .../widgets/meal_button_widget.dart | 16 +- .../presentation/widgets/meal_widget.dart | 9 +- .../diary/repositories/diary_repository.dart | 2 +- .../feed/models/plan_pagination_model.dart | 9 +- .../presentation/widgets/my_diet_widget.dart | 2 +- .../my_finance/models/card_model.dart | 3 +- .../user_cards_cubit/user_cards_cubit.dart | 25 +- .../presentation/pages/card_settings.dart | 273 ++++++++---- .../repositories/cards_repository.dart | 8 + .../my_finance/services/cards_service.dart | 5 + .../presentation/info_by_order_screen.dart | 16 +- .../widgets/collect_charge_banner.dart | 72 ++++ .../presentation/widgets/payment_banner.dart | 1 + .../presentation/pages/about_plan_screen.dart | 1 + lib/generated/intl/messages_en.dart | 27 ++ lib/generated/l10n.dart | 110 +++++ lib/l10n/intl_en.arb | 13 +- 43 files changed, 1602 insertions(+), 635 deletions(-) create mode 100644 assets/icons/star.svg create mode 100644 lib/features/create_plan/models/plan_slug_model.dart create mode 100644 lib/features/order_info/presentation/widgets/collect_charge_banner.dart diff --git a/.DS_Store b/.DS_Store index 1ea62397377fa3c9d7e919b69fb702fa9bb34479..097b747a3befbb4b0b1709860d28a9e8dc21a8f5 100644 GIT binary patch delta 36 scmZn(XbG6$&*-o*U^hRb!{h`3iOnhkg1nnm*NFhiOnhkg1q%S40#Os3@Hq$4Dk%PU{)zZ4v<&unUkNK zl#`#tz`!5?#Ew8b?>`s-Squy~_2r}+1}Ep|7JyYTu;}Aco135O;sUjV!|v72{~Ec+ eKo;RNJU2guB=rRu$c|CiY$*GTaWlKZUv>bOS44yW diff --git a/assets/icons/star.svg b/assets/icons/star.svg new file mode 100644 index 00000000..6a077d0e --- /dev/null +++ b/assets/icons/star.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index 58ccde6e..ce10badc 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -136,7 +136,10 @@ class AppRouter { GoRoute( path: CardSettingsScreen.path, name: CardSettingsScreen.name, - builder: (context, state) => const CardSettingsScreen(), + builder: (context, state) { + final chosenPlanId = state.extra as int?; + return CardSettingsScreen(chosenPlanId: chosenPlanId); + }, ), GoRoute( path: '/about-plan-screen', @@ -182,7 +185,8 @@ class AppRouter { builder: (context, state) { final data = state.extra as Map; final chatId = data['chatId'] as String; - return ChatScreen(chatId: chatId); + final sendInitialMessage = data['sendInitialMessage'] as bool; + return ChatScreen(chatId: chatId, sendInitialMessage: sendInitialMessage,); }, ), GoRoute( diff --git a/lib/core/locator_service.dart b/lib/core/locator_service.dart index 8794ce95..ebd8d539 100644 --- a/lib/core/locator_service.dart +++ b/lib/core/locator_service.dart @@ -19,6 +19,8 @@ import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_c import 'package:eatvio_app/features/create_diet/repositories/create_diet_repository.dart'; import 'package:eatvio_app/features/create_diet/services/create_diet_service.dart'; import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart'; +import 'package:eatvio_app/features/create_plan/repositories/create_plan_repository.dart'; +import 'package:eatvio_app/features/create_plan/services/create_plan_service.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_cubit.dart'; import 'package:eatvio_app/features/diets/repositories/diet_repository.dart'; import 'package:eatvio_app/features/diets/services/diet_service.dart'; @@ -117,7 +119,7 @@ Future init() async { sl.registerFactory(() => BlogPostCreateCubit(blogPostRepository: sl())); sl.registerFactory(() => SubCubit(profileRepository: sl())); sl.registerFactory(() => CreateDietCubit(profileRepository: sl(), createDietRepository: sl(), glossaryRepository: sl())); - sl.registerFactory(() => CreatePlanCubit()); + sl.registerFactory(() => CreatePlanCubit(createPlanRepository: sl())); sl.registerFactory(() => FeedProfileCubit(profileRepository: sl())); sl.registerFactory(() => AboutPlanCubit(planRepository: sl())); sl.registerFactory(() => CreateRecipeCubit(recipeRepository: sl())); @@ -158,6 +160,7 @@ Future init() async { sl.registerLazySingleton(() => CardsRepository(cardsService: sl())); sl.registerLazySingleton(() => ConsultationsRepository(consultationService: sl())); sl.registerLazySingleton(() => CancelPlanRepository(cancelService: sl())); + sl.registerLazySingleton(() => CreatePlanRepository(createPlanService: sl())); // Services sl.registerLazySingleton(() => ChatService(sl())); @@ -184,6 +187,7 @@ Future init() async { sl.registerLazySingleton(() => CardsService(sl())); sl.registerLazySingleton(() => ConsultationService(sl())); sl.registerLazySingleton(() => CancelPlanService(sl())); + sl.registerLazySingleton(() => CreatePlanService(sl())); // External sl.registerLazySingleton( diff --git a/lib/core/theme/theme_helper.dart b/lib/core/theme/theme_helper.dart index 9bed83fd..aa4e30a6 100644 --- a/lib/core/theme/theme_helper.dart +++ b/lib/core/theme/theme_helper.dart @@ -147,6 +147,8 @@ class PrimaryColors { Color get greenA70001 => const Color(0XFF00BE13); + Color get green => const Color(0XFF0BB450); + // LightGreen Color get lightGreen300 => const Color(0XFFB2DF8A); diff --git a/lib/core/widgets/custom_text_form_field.dart b/lib/core/widgets/custom_text_form_field.dart index aedfda0f..041bee70 100644 --- a/lib/core/widgets/custom_text_form_field.dart +++ b/lib/core/widgets/custom_text_form_field.dart @@ -99,7 +99,7 @@ class CustomTextFormField extends StatelessWidget { controller: controller, focusNode: focusNode, autofocus: autofocus, - style: const TextStyle(fontSize: 16, color: Colors.black, fontWeight: FontWeight.w500), + style: textStyle ?? const TextStyle(fontSize: 16, color: Colors.black, fontWeight: FontWeight.w500), obscureText: obscureText!, textInputAction: textInputAction, maxLines: maxLines, @@ -115,20 +115,17 @@ class CustomTextFormField extends StatelessWidget { prefixIconConstraints: prefixConstraints, suffixIcon: suffix, floatingLabelBehavior: FloatingLabelBehavior.always, - suffixStyle: textStyle ?? const TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: Color(0xFF323F4B)), + suffixStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: Color(0xFF323F4B)), suffixIconConstraints: suffixConstraints, isDense: false, contentPadding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12), fillColor: const Color(0xFFF6F6F6), filled: true, - border: - borderDecoration ?? + border: borderDecoration ?? OutlineInputBorder(borderRadius: BorderRadius.circular(20), borderSide: hasError ? BorderSide(color: appTheme.red, width: 1) : BorderSide.none), - enabledBorder: - borderDecoration ?? + enabledBorder: borderDecoration ?? OutlineInputBorder(borderRadius: BorderRadius.circular(20), borderSide: hasError ? BorderSide(color: appTheme.red, width: 1) : BorderSide.none), - focusedBorder: - borderDecoration ?? + focusedBorder: borderDecoration ?? OutlineInputBorder(borderRadius: BorderRadius.circular(20), borderSide: hasError ? BorderSide(color: appTheme.red, width: 1) : BorderSide.none), ), keyboardType: keyboardType, diff --git a/lib/core/widgets/success_published_diet_bottom_sheet.dart b/lib/core/widgets/success_published_diet_bottom_sheet.dart index 0145d041..223ed220 100644 --- a/lib/core/widgets/success_published_diet_bottom_sheet.dart +++ b/lib/core/widgets/success_published_diet_bottom_sheet.dart @@ -7,9 +7,10 @@ import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -class SuccessPublishedDietBottomSheet extends StatelessWidget { - const SuccessPublishedDietBottomSheet({super.key, required this.title}); +class SuccessPublishedDietOrPlanBottomSheet extends StatelessWidget { + const SuccessPublishedDietOrPlanBottomSheet({super.key, required this.title, this.isPlan = false}); final String title; + final bool isPlan; @override Widget build(BuildContext context) { final s = S.of(context); @@ -27,7 +28,7 @@ class SuccessPublishedDietBottomSheet extends StatelessWidget { style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), Text( - 'опубликованна', + isPlan ? 'опубликован' : 'опубликованна', style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), const SizedBox( diff --git a/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart b/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart index bb8e6d9f..bd53c0ac 100644 --- a/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart +++ b/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart @@ -72,7 +72,7 @@ class ChatRoomCubit extends Cubit { } } - initChat(String chatId) async { + initChat(String chatId, {bool sendInitialMessage = false}) async { try { final chatRoom = await chatRepository.getChatRoom(chatId); ChosenPlanModel? chosenPlan; @@ -92,10 +92,14 @@ class ChatRoomCubit extends Cubit { hasCard: false, ), ); - + if (chatRoom.chosenPlan?.consultantId == chatRoom.users.receiver.id) { await checkCards(); } + + if (sendInitialMessage) { + await chatRepository.sendMessage(chatId: chatId, data: 'Здравствуйте, хочу заказать у вас: ${chosenPlan?.plan.name ?? 'план'}'); + } } catch (e) { log(e.toString()); typingSubscription?.cancel(); diff --git a/lib/features/chat/presentation/pages/chat_screen.dart b/lib/features/chat/presentation/pages/chat_screen.dart index 5f14e0bf..a9403849 100644 --- a/lib/features/chat/presentation/pages/chat_screen.dart +++ b/lib/features/chat/presentation/pages/chat_screen.dart @@ -15,6 +15,7 @@ import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/features/chat/presentation/widgets/notification_banner.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; +import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart'; import 'package:eatvio_app/features/order_info/presentation/info_by_order_screen.dart'; import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart'; import 'package:eatvio_app/features/user_details/presentation/user_details_screen.dart'; @@ -28,7 +29,8 @@ import 'package:visibility_detector/visibility_detector.dart'; class ChatScreen extends StatefulWidget { final String chatId; - const ChatScreen({required this.chatId, super.key}); + final bool sendInitialMessage; + const ChatScreen({required this.chatId, super.key, required this.sendInitialMessage}); @override State createState() => _ChatScreenState(); @@ -46,7 +48,7 @@ class _ChatScreenState extends State { void initState() { super.initState(); scrollController.addListener(_onScroll); - chatRoomCubit.initChat(widget.chatId); + chatRoomCubit.initChat(widget.chatId, sendInitialMessage: widget.sendInitialMessage); } _onScroll() { @@ -96,13 +98,24 @@ class _ChatScreenState extends State { BannerType? bannerType; if (chosenPlan == null) { bannerType = null; - } else if (amIConsultant && state.hasCard) { + } else if (!amIConsultant && !state.hasCard) { bannerType = BannerType.card; } else if (amIConsultant && chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateInCoordination) { bannerType = BannerType.date; } else if (!amIConsultant && (chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateStartDateHasBeenSet || chosenPlan.chosenPlanState == ChosenPlanStateEnum.statePendingPayment)) { bannerType = BannerType.payment; + } else if (chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateDone) { + bannerType = BannerType.collectCharge; + } + + PacketOptions? chosenOption; + if (chosenPlan?.optionType == 'options1') { + chosenOption = chosenPlan?.plan.packet1Options; + } else if (chosenPlan?.optionType == 'options2') { + chosenOption = chosenPlan?.plan.packet2Options; + } else if (chosenPlan?.optionType == 'options3') { + chosenOption = chosenPlan?.plan.packet3Options; } return Scaffold( resizeToAvoidBottomInset: true, @@ -262,7 +275,7 @@ class _ChatScreenState extends State { itemCount: state.chatMessages.length + (state.isLoading ? 1 : 0), ), const SizedBox(height: 16), - if (showUserDetailsButton) ...[ + if (!showUserDetailsButton) ...[ // OrderCard( // plan: chosenPlan.plan, // isCustomerView: !amIConsultant, @@ -288,7 +301,15 @@ class _ChatScreenState extends State { ), ), if (bannerType != null) - Padding(padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), child: NotificationBanner(bannerType: bannerType)), + Padding( + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + child: NotificationBanner( + bannerType: bannerType, + chosenPlanId: chosenPlan?.id, + planName: chosenPlan?.plan.name, + planPrice: chosenOption?.price.toDouble(), + ), + ), ], ], ), diff --git a/lib/features/chat/presentation/widgets/notification_banner.dart b/lib/features/chat/presentation/widgets/notification_banner.dart index 0059f7cc..d4eaf577 100644 --- a/lib/features/chat/presentation/widgets/notification_banner.dart +++ b/lib/features/chat/presentation/widgets/notification_banner.dart @@ -1,5 +1,6 @@ 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_outlined_button.dart'; import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart'; import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.dart'; @@ -13,8 +14,11 @@ import 'package:flutter_svg/svg.dart'; import 'package:go_router/go_router.dart'; class NotificationBanner extends StatefulWidget { - const NotificationBanner({super.key, required this.bannerType}); + const NotificationBanner({super.key, required this.bannerType, this.planPrice, this.planName, this.chosenPlanId}); final BannerType bannerType; + final double? planPrice; + final String? planName; + final int? chosenPlanId; @override State createState() => _NotificationBannerState(); @@ -31,96 +35,145 @@ class _NotificationBannerState extends State { color: appTheme.gray50, borderRadius: BorderRadius.circular(20), ), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, + child: Column( children: [ - Padding( - padding: const EdgeInsets.only(top: 12, right: 8, left: 12), - child: SvgPicture.asset('assets/icons/reversed_exclamation_mark.svg', height: 68, width: 68), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (widget.bannerType != BannerType.collectCharge) + Padding( + padding: const EdgeInsets.only(top: 12, right: 8, left: 12), + child: SvgPicture.asset('assets/icons/reversed_exclamation_mark.svg', height: 68, width: 68), + ) + else + const Padding( + padding: EdgeInsets.only(top: 12, right: 8, left: 12), + child: _StarIcon(), + ), + if (isLoading) + const Padding( + padding: EdgeInsets.only(left: 40, top: 24), + child: Loader(), + ) + else + Padding( + padding: const EdgeInsets.only(top: 16), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: switch (widget.bannerType) { + BannerType.date => [ + SizedBox( + width: size.width * 0.6, + child: Text(s.choose_plan_start_date, style: TextStyles.textMMedium.copyWith(color: appTheme.black)), + ), + const SizedBox(height: 12), + GestureDetector( + onTap: () { + final cubit = context.read(); + showModalBottomSheet( + context: context, + builder: (context) => ChosePlanDateWidget( + chatCubit: cubit, + ), + ); + }, + child: Text(s.choose_date, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)), + ), + const SizedBox(height: 16), + ], + BannerType.card => [ + SizedBox( + width: size.width * 0.6, + child: Text(s.can_not_choose_start_date, style: TextStyles.textMMedium.copyWith(color: appTheme.black)), + ), + const SizedBox(height: 12), + GestureDetector( + onTap: () { + GoRouter.of(context).pushReplacement( + CardSettingsScreen.path, + ); + }, + child: Text(s.add_card, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)), + ), + const SizedBox(height: 16), + ], + BannerType.payment => [ + SizedBox( + width: size.width * 0.6, + child: Text(s.tap_on_link_to_pay, style: TextStyles.textMMedium.copyWith(color: appTheme.black)), + ), + const SizedBox(height: 12), + GestureDetector( + onTap: () async { + final planId = (context.read().state as ChatRoomStateLoaded).chatRoom.chosenPlan?.id; + setState(() { + isLoading = true; + }); + final paymentLink = await context.read().getPaymentLink(); + setState(() { + isLoading = false; + }); + if (paymentLink != null && planId != null) { + if (!context.mounted) return; + GoRouter.of(context).pushReplacement( + PaymentScreen.path, + extra: {'url': paymentLink, 'planId': planId}, + ); + } + }, + child: Text(s.pay, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)), + ), + const SizedBox(height: 16), + ], + BannerType.collectCharge => [ + SizedBox( + width: size.width * 0.6, + child: + Text('${s.congratsYouHaveFinishedPlan} «${widget.planName}»', style: TextStyles.textMSemibold.copyWith(color: appTheme.black)), + ), + const SizedBox(height: 8), + SizedBox( + width: size.width * 0.6, + child: Text('${s.userConfirmedPlanCompletion} ${widget.planPrice}₽', style: TextStyles.textSMedium.copyWith(color: appTheme.black)), + ), + const SizedBox(height: 8), + ], + }, + ), + ), + ], ), - if (isLoading) - const Padding( - padding: EdgeInsets.only(left: 40, top: 24), - child: Loader(), - ) - else + if (widget.bannerType == BannerType.collectCharge) Padding( - padding: const EdgeInsets.only(top: 16), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: switch (widget.bannerType) { - BannerType.date => [ - SizedBox( - width: size.width * 0.6, - child: Text(s.choose_plan_start_date, style: TextStyles.textMMedium.copyWith(color: appTheme.black)), - ), - const SizedBox(height: 12), - GestureDetector( - onTap: () { - final cubit = context.read(); - showModalBottomSheet( - context: context, - builder: (context) => ChosePlanDateWidget( - chatCubit: cubit, - ), - ); - }, - child: Text(s.choose_date, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)), - ), - const SizedBox(height: 16), - ], - BannerType.card => [ - SizedBox( - width: size.width * 0.6, - child: Text(s.can_not_choose_start_date, style: TextStyles.textMMedium.copyWith(color: appTheme.black)), - ), - const SizedBox(height: 12), - GestureDetector( - onTap: () { - GoRouter.of(context).pushReplacement( - CardSettingsScreen.path, - ); - }, - child: Text(s.add_card, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)), - ), - const SizedBox(height: 16), - ], - BannerType.payment => [ - SizedBox( - width: size.width * 0.6, - child: Text(s.tap_on_link_to_pay, style: TextStyles.textMMedium.copyWith(color: appTheme.black)), - ), - const SizedBox(height: 12), - GestureDetector( - onTap: () async { - final planId = (context.read().state as ChatRoomStateLoaded).chatRoom.chosenPlan?.id; - setState(() { - isLoading = true; - }); - final paymentLink = await context.read().getPaymentLink(); - setState(() { - isLoading = false; - }); - if (paymentLink != null && planId != null) { - if (!context.mounted) return; - GoRouter.of(context).pushReplacement( - PaymentScreen.path, - extra: {'url': paymentLink, 'planId': planId}, - ); - } - }, - child: Text(s.pay, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)), - ), - const SizedBox(height: 16), - ], + padding: const EdgeInsets.symmetric(horizontal: 12), + child: CustomOutlinedButton( + text: s.collectCharge, + height: 52, + buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), + buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.green)), + onPressed: () async { + GoRouter.of(context).push(CardSettingsScreen.path, extra: widget.chosenPlanId); }, ), - ), + ) ], ), ); } } -enum BannerType { date, card, payment } +class _StarIcon extends StatelessWidget { + const _StarIcon(); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration(color: appTheme.whiteA700, shape: BoxShape.circle), + child: SvgPicture.asset('assets/icons/star.svg'), + ); + } +} + +enum BannerType { date, card, payment, collectCharge } diff --git a/lib/features/create_diet/presentation/create_diet_screen.dart b/lib/features/create_diet/presentation/create_diet_screen.dart index a8d2e019..48d1d27e 100644 --- a/lib/features/create_diet/presentation/create_diet_screen.dart +++ b/lib/features/create_diet/presentation/create_diet_screen.dart @@ -72,7 +72,7 @@ class _CreateDietScreenContentState extends State { showBottomSheetWithBlur( userRootNavigator: true, context: context, - child: SuccessPublishedDietBottomSheet( + child: SuccessPublishedDietOrPlanBottomSheet( title: state.dietData.name ?? 'Диета', ), ); @@ -83,7 +83,7 @@ class _CreateDietScreenContentState extends State { context.pop(); showBottomSheetWithBlur( context: context, - child: SuccessPublishedDietBottomSheet( + child: SuccessPublishedDietOrPlanBottomSheet( title: '${state.dietData.name ?? 'Диета'} сохраненна в черновиках', ), ); @@ -193,8 +193,8 @@ class _CreateDietScreenContentState extends State { context.read().setDietState(DietState.draft); if (widget.diet != null) { final result = await context.read().updateDiet( - widget.diet!.id, - ); + widget.diet!.id, + ); if (result == 'error') { if (!context.mounted) return; showErrorDialog(context, title: '${s.could_not_update_diet} ${state.dietData.name}'); diff --git a/lib/features/create_plan/models/create_plan_model.dart b/lib/features/create_plan/models/create_plan_model.dart index 75187284..4b85ffba 100644 --- a/lib/features/create_plan/models/create_plan_model.dart +++ b/lib/features/create_plan/models/create_plan_model.dart @@ -15,8 +15,8 @@ class CreatePlanModel with _$CreatePlanModel { const factory CreatePlanModel({ String? name, String? details, - String? state, - int? duration, + @Default('active') String? state, + int? duration, required List type, XFile? image, required List options, @@ -25,9 +25,12 @@ class CreatePlanModel with _$CreatePlanModel { Future> toCreationJson() async => { 'name': name, 'duration': duration, - 'state': state, - 'type': type, + 'state': 'active', 'description_md': details, + 'type[slim]': type[0] ? '1' : '0', + 'type[fit]': type[1] ? '1' : '0', + 'type[wellness]': type[2] ? '1' : '0', + 'type[behavior]': type[3] ? '1' : '0', 'image': image != null ? await MultipartFile.fromFile((await ImageCompressUtility.compressFile(File(image!.path))).path, filename: image!.name) : null, }; diff --git a/lib/features/create_plan/models/plan_slug_model.dart b/lib/features/create_plan/models/plan_slug_model.dart new file mode 100644 index 00000000..b440cb07 --- /dev/null +++ b/lib/features/create_plan/models/plan_slug_model.dart @@ -0,0 +1,11 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'plan_slug_model.freezed.dart'; +part 'plan_slug_model.g.dart'; + +@freezed +class PlanSlugModel with _$PlanSlugModel { + factory PlanSlugModel({@JsonKey(name: 'slug') required String slug}) = _PlanSlugModel; + + factory PlanSlugModel.fromJson(Map json) => _$PlanSlugModelFromJson(json); +} diff --git a/lib/features/create_plan/presentation/create_plan_screen.dart b/lib/features/create_plan/presentation/create_plan_screen.dart index ab921880..c9ac8302 100644 --- a/lib/features/create_plan/presentation/create_plan_screen.dart +++ b/lib/features/create_plan/presentation/create_plan_screen.dart @@ -1,9 +1,13 @@ 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/widgets/bottom_sheet_with_blur.dart'; import 'package:eatvio_app/core/widgets/custom_outlined_button.dart'; import 'package:eatvio_app/core/widgets/custom_text_form_field.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/success_published_diet_bottom_sheet.dart'; +import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart'; import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart'; import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart'; import 'package:eatvio_app/features/create_plan/presentation/widgets/option_widget.dart'; @@ -15,6 +19,7 @@ import 'package:eatvio_app/features/create_plan/presentation/widgets/select_exac import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; class CreatePlanScreen extends StatelessWidget { static const name = 'createPlanScreen'; @@ -36,177 +41,229 @@ class CreatePlanScreenContent extends StatelessWidget { @override Widget build(BuildContext context) { final s = S.of(context); - return Scaffold( - backgroundColor: appTheme.gray100, - appBar: AppBar( - centerTitle: true, - title: Text( - s.adding_plan, - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - softWrap: true, - style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), - ), - ), - body: Stack( - children: [ - SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 8), - MainBackgroundBouble( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 16), - Text( - s.label, - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - softWrap: true, - style: TextStyles.textSMedium.copyWith(color: appTheme.black), - ), - const SizedBox(height: 4), - CustomTextFormField( - hintText: s.plan_label_desc, - onChanged: (value) { - context.read().setName(value); - }, - maxLength: 150, - ), - const SizedBox(height: 12), - Text( - s.plan_duration, - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - softWrap: true, - style: TextStyles.textSMedium.copyWith(color: appTheme.black), - ), - const SizedBox(height: 4), - const PlanDurationDropDownWidget(), - const SizedBox(height: 20), - const PlanImagePickerWidget(), - const SizedBox(height: 16), - const PlanTypeChoiceWidget(), - const SizedBox(height: 16), - Text( - s.plan_desc, - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - softWrap: true, - style: TextStyles.textSMedium.copyWith(color: appTheme.black), - ), - const SizedBox(height: 4), - SizedBox( - height: 132, - child: CustomTextFormField( - textAlignVertical: TextAlignVertical.top, - maxLength: 1000, - maxLines: null, - minLines: null, - expands: true, - hintText: s.plan_desc_example, - onChanged: (value) { - context.read().setDetails(value); - }, - ), - ), - const SizedBox(height: 16), - ], - ), - ), - const SizedBox(height: 20), - Padding( - padding: const EdgeInsets.only(left: 16), - child: Text( - s.services, - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - softWrap: true, - style: TextStyles.headingXLSemibold.copyWith(color: appTheme.blueGray400), - ), - ), - const SizedBox(height: 8), - const OptionsCountButton(), - const SizedBox(height: 20), - BlocBuilder( - buildWhen: (previous, current) => previous.selectedOptionIndex != current.selectedOptionIndex, - builder: (context, state) { - if (state.selectedOptionIndex == null) { - return const SizedBox.shrink(); - } - return Padding( - padding: const EdgeInsets.only(left: 16), - child: Text( - '${s.option} ${(state.selectedOptionIndex ?? 0) + 1}', - style: TextStyles.textLSemibold.copyWith(color: appTheme.blueGray400), - ), - ); - }, - ), - const SizedBox(height: 8), - const OptionWidget(), - const SizedBox(height: 20), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Column( - children: [ - CustomOutlinedButton( - onPressed: () {}, - text: s.to_draft, - height: 52, - buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray), - buttonStyle: ButtonStyle( - side: const WidgetStatePropertyAll(BorderSide.none), - backgroundColor: WidgetStatePropertyAll(appTheme.dottedBorder), - ), - ), - const SizedBox(height: 12), - CustomOutlinedButton( - onPressed: () async {}, - text: s.preview, - height: 52, - buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray), - buttonStyle: ButtonStyle( - side: const WidgetStatePropertyAll(BorderSide.none), - backgroundColor: WidgetStatePropertyAll(appTheme.dottedBorder), - ), - ), - const SizedBox(height: 12), - CustomOutlinedButton( - onPressed: () {}, - text: s.publish, - height: 52, - buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), - buttonStyle: ButtonStyle( - side: const WidgetStatePropertyAll(BorderSide.none), - backgroundColor: WidgetStatePropertyAll(appTheme.blueA400), - ), - ), - ], - ), - ), - ], + return BlocBuilder( + buildWhen: (previous, current) => previous.planCreated != current.planCreated || previous.isLoading != current.isLoading, + builder: (context, state) { + if (state.planCreated) { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (!context.mounted) return; + context.pop(); + showBottomSheetWithBlur( + userRootNavigator: true, + context: context, + child: SuccessPublishedDietOrPlanBottomSheet( + title: state.planData.name ?? 'План', + isPlan: true, + ), + ); + }); + } + return Scaffold( + backgroundColor: appTheme.gray100, + appBar: AppBar( + centerTitle: true, + title: Text( + s.adding_plan, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + softWrap: true, + style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), ), - BlocBuilder( - builder: (context, state) { - if (state.selectedOptionIndex == null) { - return const SizedBox.shrink(); - } - return const Padding( - padding: EdgeInsets.only(bottom: 24.0), - child: Align( - alignment: Alignment.bottomCenter, - child: SelectExactOptionWidget(), + body: Stack( + children: [ + SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 8), + MainBackgroundBouble( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 16), + Text( + s.label, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + softWrap: true, + style: TextStyles.textSMedium.copyWith(color: appTheme.black), + ), + const SizedBox(height: 4), + BlocBuilder( + buildWhen: (previous, current) => previous.validationState != current.validationState, + builder: (context, state) { + return Column( + children: [ + CustomTextFormField( + hintText: s.plan_label_desc, + hasError: state.validationState.titleHasError, + onChanged: (value) { + context.read().setName(value); + }, + maxLength: 150, + ), + if (state.validationState.titleHasError) ...[ + const SizedBox(height: 4), + ValidationErrorWidget(error: s.atLeast3Symbols), + ], + ], + ); + }, + ), + const SizedBox(height: 12), + Text( + s.plan_duration, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + softWrap: true, + style: TextStyles.textSMedium.copyWith(color: appTheme.black), + ), + const SizedBox(height: 4), + const PlanDurationDropDownWidget(), + const SizedBox(height: 20), + const PlanImagePickerWidget(), + const SizedBox(height: 16), + const PlanTypeChoiceWidget(), + const SizedBox(height: 16), + Text( + s.plan_desc, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + softWrap: true, + style: TextStyles.textSMedium.copyWith(color: appTheme.black), + ), + const SizedBox(height: 4), + BlocBuilder( + buildWhen: (previous, current) => previous.validationState != current.validationState, + builder: (context, state) { + return Column( + children: [ + SizedBox( + height: 132, + child: CustomTextFormField( + hasError: state.validationState.descriptionHasError, + textAlignVertical: TextAlignVertical.top, + maxLength: 1000, + maxLines: null, + minLines: null, + expands: true, + hintText: s.plan_desc_example, + onChanged: (value) { + context.read().setDetails(value); + }, + ), + ), + if (state.validationState.descriptionHasError) ...[ + const SizedBox(height: 4), + ValidationErrorWidget(error: s.atLeast3Symbols), + ], + ], + ); + }, + ), + const SizedBox(height: 16), + ], + ), + ), + const SizedBox(height: 20), + Padding( + padding: const EdgeInsets.only(left: 16), + child: Text( + s.services, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + softWrap: true, + style: TextStyles.headingXLSemibold.copyWith(color: appTheme.blueGray400), + ), + ), + const SizedBox(height: 8), + const OptionsCountButton(), + const SizedBox(height: 20), + BlocBuilder( + buildWhen: (previous, current) => previous.selectedOptionIndex != current.selectedOptionIndex, + builder: (context, state) { + if (state.selectedOptionIndex == null) { + return const SizedBox.shrink(); + } + return Padding( + padding: const EdgeInsets.only(left: 16), + child: Text( + '${s.option} ${(state.selectedOptionIndex ?? 0) + 1}', + style: TextStyles.textLSemibold.copyWith(color: appTheme.blueGray400), + ), + ); + }, + ), + const SizedBox(height: 8), + const OptionWidget(), + const SizedBox(height: 20), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Column( + children: [ + // CustomOutlinedButton( + // onPressed: () {}, + // text: s.to_draft, + // height: 52, + // buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray), + // buttonStyle: ButtonStyle( + // side: const WidgetStatePropertyAll(BorderSide.none), + // backgroundColor: WidgetStatePropertyAll(appTheme.dottedBorder), + // ), + // ), + // const SizedBox(height: 12), + // CustomOutlinedButton( + // onPressed: () async {}, + // text: s.preview, + // height: 52, + // buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray), + // buttonStyle: ButtonStyle( + // side: const WidgetStatePropertyAll(BorderSide.none), + // backgroundColor: WidgetStatePropertyAll(appTheme.dottedBorder), + // ), + // ), + // const SizedBox(height: 12), + state.isLoading + ? const Loader() + : CustomOutlinedButton( + onPressed: () async { + await context.read().publishPlan(); + }, + text: s.publish, + height: 52, + buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), + buttonStyle: ButtonStyle( + side: const WidgetStatePropertyAll(BorderSide.none), + backgroundColor: WidgetStatePropertyAll(appTheme.blueA400), + ), + ), + ], + ), + ), + const SizedBox(height: 80), + ], ), - ); - }, + ), + BlocBuilder( + builder: (context, state) { + if (state.selectedOptionIndex == null) { + return const SizedBox.shrink(); + } + return const Padding( + padding: EdgeInsets.only(bottom: 24.0), + child: Align( + alignment: Alignment.bottomCenter, + child: SelectExactOptionWidget(), + ), + ); + }, + ), + ], ), - ], - ), + ); + }, ); } } diff --git a/lib/features/create_plan/presentation/cubit/create_plan_cubit.dart b/lib/features/create_plan/presentation/cubit/create_plan_cubit.dart index 8d406a71..7239c657 100644 --- a/lib/features/create_plan/presentation/cubit/create_plan_cubit.dart +++ b/lib/features/create_plan/presentation/cubit/create_plan_cubit.dart @@ -1,56 +1,65 @@ import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart'; +import 'package:eatvio_app/features/create_plan/repositories/create_plan_repository.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_image_compress/flutter_image_compress.dart'; class CreatePlanCubit extends Cubit { - CreatePlanCubit() : super(CreatePlanState.initial()); + final CreatePlanRepository createPlanRepository; + CreatePlanCubit({required this.createPlanRepository}) : super(CreatePlanState.initial()); void setDetails(String text) { emit(state.copyWith(planData: state.planData.copyWith(details: text))); + validateDetails(); } void setName(String name) { emit(state.copyWith(planData: state.planData.copyWith(name: name))); + validateName(); } void setOptionTitle(String title) { final options = [...state.planData.options]; - options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(short: title); + options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(short: title); emit(state.copyWith(planData: state.planData.copyWith(options: options))); + validateSelectedOptionTitle(); } - void setPrice(double price){ + void setPrice(double price) { final selectedOptionIndex = state.selectedOptionIndex ?? 0; - final options = [...state.planData.options]; - options[selectedOptionIndex] = options[selectedOptionIndex].copyWith(price: price); + final options = [...state.planData.options]; + options[selectedOptionIndex] = options[selectedOptionIndex].copyWith(price: price); emit(state.copyWith(planData: state.planData.copyWith(options: options))); + validateSelectedOptionPrice(); } void setOptionDescription(String description) { final options = [...state.planData.options]; - options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(description: description); + options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(description: description); emit(state.copyWith(planData: state.planData.copyWith(options: options))); + validateSelectedOptionDescription(); } - void setOptionContent(String content, index) { - final options = [...state.planData.options]; + void setOptionContent(String content, index) { + final options = [...state.planData.options]; final items = [...options[state.selectedOptionIndex ?? 0].items]; if (items.length <= index) { items.add(content); - }else { + } else { items[index] = content; } - options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(items: items); + options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(items: items); emit(state.copyWith(planData: state.planData.copyWith(options: options))); + validateSelectedOptionItems(); } void removeOptionContentItem(int index) { final options = [...state.planData.options]; final updatedItems = [...options[state.selectedOptionIndex ?? 0].items]; updatedItems.removeAt(index); - options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(items: updatedItems); + options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(items: updatedItems); emit(state.copyWith(planData: state.planData.copyWith(options: options))); + validateSelectedOptionItems(); } void setDuration(int index) { @@ -62,10 +71,12 @@ class CreatePlanCubit extends Cubit { ), ), ); + validateDuration(); } void setImage(XFile? image) { emit(state.copyWith(planData: state.planData.copyWith(image: image))); + validateImage(); } void setPlanState(String planState) { @@ -74,9 +85,319 @@ class CreatePlanCubit extends Cubit { void setPlanType(List type) { emit(state.copyWith(planData: state.planData.copyWith(type: type))); + if (type.any((elem) => elem)) { + validateType(); + } } void setSelectedOptionIndex(int? index) { emit(state.copyWith(selectedOptionIndex: index)); } + + bool validateName() { + final name = state.planData.name; + if (name == null || name.length < 3) { + emit(state.copyWith.validationState(titleHasError: true)); + return false; + } + emit(state.copyWith.validationState(titleHasError: false)); + return true; + } + + bool validateDetails() { + final details = state.planData.details; + if (details == null || details.length < 3) { + emit(state.copyWith.validationState(descriptionHasError: true)); + return false; + } + emit(state.copyWith.validationState(descriptionHasError: false)); + return true; + } + + bool validateDuration() { + if (state.planData.duration == null) { + emit(state.copyWith.validationState(durationHasError: true)); + return false; + } + emit(state.copyWith.validationState(durationHasError: false)); + return true; + } + + bool validateType() { + final type = state.planData.type; + if (type.isEmpty || type.every((elem) => elem)) { + emit(state.copyWith.validationState(typeHasError: true)); + return false; + } + emit(state.copyWith.validationState(typeHasError: false)); + return true; + } + + bool validateImage() { + if (state.planData.image == null) { + emit(state.copyWith.validationState(imageHasError: true)); + return false; + } + emit(state.copyWith.validationState(imageHasError: false)); + return true; + } + + bool validateSelectedOptionPrice() { + final option = state.planData.options[state.selectedOptionIndex ?? 0]; + final optionsValidationsStates = [...state.validationState.optionsValidationState]; + if (option.price < 1200) { + optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(priceHasError: true); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + return false; + } + optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(priceHasError: false); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + return true; + } + + bool validatePriceOfAllFilledOptions() { + final options = state.planData.options; + final optionsValidationsStates = [...state.validationState.optionsValidationState]; + bool result = true; + if (options[0].price < 1200) { + optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(priceHasError: true); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + result = false; + } else { + optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(priceHasError: false); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + } + + if (options[1].price < 1200) { + optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(priceHasError: true); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + result = false; + } else { + optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(priceHasError: false); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + } + + if (options[2].price < 1200) { + optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(priceHasError: true); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + result = false; + } else { + optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(priceHasError: false); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + } + return result; + } + + bool validateSelectedOptionItems() { + final option = state.planData.options[state.selectedOptionIndex ?? 0]; + final optionsValidationsStates = [...state.validationState.optionsValidationState]; + bool result = true; + if (option.items.isEmpty) { + optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(isItemsEmpty: true); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + return false; + } + final itemsErrors = Map.from(optionsValidationsStates[state.selectedOptionIndex ?? 0].itemsErrors); + optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(isItemsEmpty: false); + for (final (index, item) in option.items.indexed) { + if (item.length < 3) { + itemsErrors[index] = true; + result = false; + } else { + itemsErrors[index] = false; + } + } + optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(itemsErrors: itemsErrors); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + return result; + } + + bool validateItemsOfAllFilledOptions() { + final options = state.planData.options; + final optionsValidationsStates = [...state.validationState.optionsValidationState]; + bool result = true; + + if (options[0].items.isEmpty) { + optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(isItemsEmpty: true); + result = false; + } else { + optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(isItemsEmpty: false); + } + + final option1ItemsErrors = Map.from(optionsValidationsStates[0].itemsErrors); + for (final (index, item) in options[0].items.indexed) { + if (item.length < 3) { + option1ItemsErrors[index] = true; + result = false; + } else { + option1ItemsErrors[index] = false; + } + } + + optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(itemsErrors: option1ItemsErrors); + if (state.selectedOptionIndex == null) { + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + return result; + } + if (options[1].items.isEmpty) { + optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(isItemsEmpty: true); + result = false; + } else { + optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(isItemsEmpty: false); + } + + final option2ItemsErrors = Map.from(optionsValidationsStates[1].itemsErrors); + for (final (index, item) in options[1].items.indexed) { + if (item.length < 3) { + option2ItemsErrors[index] = true; + result = false; + } else { + option2ItemsErrors[index] = false; + } + } + + optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(itemsErrors: option2ItemsErrors); + + if (options[2].items.isEmpty) { + optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(isItemsEmpty: true); + result = false; + } else { + optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(isItemsEmpty: false); + } + + final option3ItemsErrors = Map.from(optionsValidationsStates[2].itemsErrors); + for (final (index, item) in options[2].items.indexed) { + if (item.length < 3) { + option3ItemsErrors[index] = true; + result = false; + } else { + option3ItemsErrors[index] = false; + } + } + optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(itemsErrors: option3ItemsErrors); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + return result; + } + + bool validateSelectedOptionTitle() { + final option = state.planData.options[state.selectedOptionIndex ?? 0]; + final optionsValidationsStates = [...state.validationState.optionsValidationState]; + if (option.short.length < 3) { + optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(titleHasError: true); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + return false; + } + optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(titleHasError: false); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + return true; + } + + bool validateTitlesOfAllFilledOptions() { + final options = state.planData.options; + final optionsValidationsStates = [...state.validationState.optionsValidationState]; + bool result = true; + if (options[0].short.length < 3) { + optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(titleHasError: true); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + result = false; + } else { + optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(titleHasError: false); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + } + if (state.selectedOptionIndex == null) return result; + + if (options[1].short.length < 3) { + optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(titleHasError: true); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + result = false; + } else { + optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(titleHasError: false); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + } + + if (options[2].short.length < 3) { + optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(titleHasError: true); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + result = false; + } else { + optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(titleHasError: false); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + } + return result; + } + + bool validateSelectedOptionDescription() { + final option = state.planData.options[state.selectedOptionIndex ?? 0]; + final optionsValidationsStates = [...state.validationState.optionsValidationState]; + if (option.description.length < 3) { + optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(descriptionHasError: true); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + return false; + } + optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(descriptionHasError: false); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + return true; + } + + bool validateDescriptionsOfAllOptions() { + final options = state.planData.options; + final optionsValidationsStates = [...state.validationState.optionsValidationState]; + bool result = true; + + if (options[0].description.length < 3) { + optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(descriptionHasError: true); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + result = false; + } else { + optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(descriptionHasError: false); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + } + if (state.selectedOptionIndex == null) return result; + if (options[1].description.length < 3) { + optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(descriptionHasError: true); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + result = false; + } else { + optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(descriptionHasError: false); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + } + + if (options[2].description.length < 3) { + optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(descriptionHasError: true); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + result = false; + } else { + optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(descriptionHasError: false); + emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); + } + return result; + } + + bool validateAllFields() { + final isNameValid = validateName(); + final isDetailsValid = validateDetails(); + final isImageValid = validateImage(); + final isDurationValid = validateDuration(); + final areTitlesValid = validateTitlesOfAllFilledOptions(); + final arePricesValid = validatePriceOfAllFilledOptions(); + final areDescriptionsValid = validateDescriptionsOfAllOptions(); + final areItemsValid = validateItemsOfAllFilledOptions(); + return isNameValid && isDetailsValid && isImageValid && isDurationValid && areTitlesValid && arePricesValid && areDescriptionsValid && areItemsValid; + } + + Future publishPlan() async { + if (validateAllFields()) { + emit(state.copyWith(isLoading: true)); + final planModel = await state.planData.toCreationJson(); + final plan = await createPlanRepository.createPlan(model: planModel); + if (state.planData.options[1].short.isNotEmpty) { + final options = state.planData.toOptionsCreationJson(); + await createPlanRepository.createPacketOptions(slug: plan.slug, model: options); + } else { + final option = state.planData.toOptionCreationJson(); + await createPlanRepository.createPacketOption(slug: plan.slug, model: option); + } + emit(state.copyWith(isLoading: false, planCreated: true)); + } + } } diff --git a/lib/features/create_plan/presentation/cubit/create_plan_state.dart b/lib/features/create_plan/presentation/cubit/create_plan_state.dart index 684df959..7a70be54 100644 --- a/lib/features/create_plan/presentation/cubit/create_plan_state.dart +++ b/lib/features/create_plan/presentation/cubit/create_plan_state.dart @@ -9,7 +9,9 @@ class CreatePlanState with _$CreatePlanState { const factory CreatePlanState({ required bool isLoading, required String? errorMessage, - required CreatePlanModel planData, + required CreatePlanModel planData, + required PlanValidationState validationState, + required bool planCreated, int? durationIndex, int? selectedOptionIndex, }) = _CreatePlanState; @@ -17,9 +19,55 @@ class CreatePlanState with _$CreatePlanState { factory CreatePlanState.initial() => CreatePlanState( isLoading: false, errorMessage: null, + planCreated: false, + validationState: PlanValidationState.empty(), planData: CreatePlanModel( type: [false, false, false, false], options: [PacketOptions.empty(), PacketOptions.empty(), PacketOptions.empty()], - ), + ), + ); +} + +@freezed +class PlanValidationState with _$PlanValidationState { + factory PlanValidationState({ + required bool titleHasError, + required bool durationHasError, + required bool typeHasError, + required bool descriptionHasError, + required bool imageHasError, + required List optionsValidationState, + }) = _PlanValidationState; + + factory PlanValidationState.empty() => PlanValidationState( + titleHasError: false, + durationHasError: false, + typeHasError: false, + descriptionHasError: false, + imageHasError: false, + optionsValidationState: [ + OptionValidationState.empty(), + OptionValidationState.empty(), + OptionValidationState.empty(), + ], + ); +} + +@freezed +class OptionValidationState with _$OptionValidationState { + factory OptionValidationState({ + required bool titleHasError, + required bool priceHasError, + required bool descriptionHasError, + required Map itemsErrors, + required bool isItemsEmpty, + }) = _OptionValidationState; + + factory OptionValidationState.empty() => OptionValidationState( + descriptionHasError: false, + titleHasError: false, + priceHasError: false, + isItemsEmpty: false, + itemsErrors: {}, ); } diff --git a/lib/features/create_plan/presentation/widgets/add_item_widget.dart b/lib/features/create_plan/presentation/widgets/add_item_widget.dart index 16a79d26..e51cf977 100644 --- a/lib/features/create_plan/presentation/widgets/add_item_widget.dart +++ b/lib/features/create_plan/presentation/widgets/add_item_widget.dart @@ -1,6 +1,8 @@ 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/create_plan/presentation/cubit/create_plan_cubit.dart'; +import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -39,34 +41,53 @@ class _AddItemWidgetState extends State { if (widget.items.length > widget.index && controller.text != widget.items[widget.index]) { controller.text = widget.items[widget.index]; } - return Row( - children: [ - Expanded( - child: CustomTextFormField( - controller: controller, - hintText: widget.enableHint ? s.option_content_hint_text : null, - onChanged: (value) { - context.read().setOptionContent(value, widget.index); - }, - ), - ), - if (widget.canBeDeleted) ...[ - const SizedBox(width: 8), - GestureDetector( - onTap: () { - context.read().removeOptionContentItem(widget.index); - }, - child: Container( - padding: const EdgeInsets.all(14), - decoration: BoxDecoration( - color: appTheme.lightGray0, - borderRadius: BorderRadius.circular(20), - ), - child: SvgPicture.asset('assets/icons/trash_can.svg', width: 20, height: 24), + return BlocBuilder( + buildWhen: (previous, current) => previous.validationState != current.validationState, + builder: (context, state) { + final hasError = state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].isItemsEmpty || + (state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].itemsErrors[widget.index] ?? false); + return Column( + children: [ + Row( + children: [ + Expanded( + child: CustomTextFormField( + controller: controller, + hasError: hasError, + hintText: widget.enableHint ? s.option_content_hint_text : null, + onChanged: (value) { + context.read().setOptionContent(value, widget.index); + }, + ), + ), + if (widget.canBeDeleted) ...[ + const SizedBox(width: 8), + GestureDetector( + onTap: () { + context.read().removeOptionContentItem(widget.index); + }, + child: Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: appTheme.lightGray0, + borderRadius: BorderRadius.circular(20), + ), + child: SvgPicture.asset('assets/icons/trash_can.svg', width: 20, height: 24), + ), + ), + ], + ], ), - ), - ], - ], + if (state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].isItemsEmpty) ...[ + const SizedBox(height: 4), + ValidationErrorWidget(error: s.addAtLeastOneItem) + ] else if (state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].itemsErrors[widget.index] ?? false) ...[ + const SizedBox(height: 4), + ValidationErrorWidget(error: s.atLeast3Symbols) + ] + ], + ); + }, ); } } diff --git a/lib/features/create_plan/presentation/widgets/option_widget.dart b/lib/features/create_plan/presentation/widgets/option_widget.dart index 42d52977..fffa43b7 100644 --- a/lib/features/create_plan/presentation/widgets/option_widget.dart +++ b/lib/features/create_plan/presentation/widgets/option_widget.dart @@ -3,6 +3,7 @@ import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/widgets/custom_divider.dart'; import 'package:eatvio_app/core/widgets/custom_text_form_field.dart'; import 'package:eatvio_app/core/widgets/main_background_bouble.dart'; +import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart'; import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart'; import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart'; import 'package:eatvio_app/features/create_plan/presentation/widgets/add_item_widget.dart'; @@ -32,35 +33,9 @@ class _OptionWidgetState extends State { super.initState(); customerPriceController.addListener(() { if (!customerPriceController.text.endsWith('₽')) { - final textWithoutCurrency = customerPriceController.text.replaceAll('₽', ''); - customerPriceController.value = TextEditingValue( - text: "$textWithoutCurrency₽", - selection: TextSelection.collapsed(offset: textWithoutCurrency.length), - ); - } - final price = double.tryParse(customerPriceController.text.replaceAll('₽', '')); - if (price != null && price < 1200) { - customerPriceController.value = const TextEditingValue( - text: "1200₽", - selection: TextSelection.collapsed(offset: 4), - ); - } - }); - - creatorPriceController.addListener(() { - if (!creatorPriceController.text.endsWith('₽')) { - final textWithoutCurrency = creatorPriceController.text.replaceAll('₽', ''); - creatorPriceController.value = TextEditingValue( - text: "$textWithoutCurrency₽", - selection: TextSelection.collapsed(offset: textWithoutCurrency.length), - ); - } - final price = double.tryParse(creatorPriceController.text.replaceAll('₽', '')); - if (price != null && price < 1000) { - creatorPriceController.value = const TextEditingValue( - text: "1000₽", - selection: TextSelection.collapsed(offset: 4), - ); + final text = customerPriceController.text.replaceAll('₽', ''); + customerPriceController.text = '$text₽'; + customerPriceController.selection = TextSelection.collapsed(offset: text.length); } }); } @@ -94,12 +69,17 @@ class _OptionWidgetState extends State { const SizedBox(height: 4), CustomTextFormField( hintText: optionNameHintText, + hasError: state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].titleHasError, controller: optionTitleController, onChanged: (value) { context.read().setOptionTitle(value); }, maxLength: 150, ), + if (state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].titleHasError) ...[ + const SizedBox(height: 4), + ValidationErrorWidget(error: s.atLeast3Symbols), + ], const SizedBox(height: 12), Text( s.option_desc, @@ -114,6 +94,7 @@ class _OptionWidgetState extends State { child: CustomTextFormField( controller: optionDescriptionController, textAlignVertical: TextAlignVertical.top, + hasError: state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].descriptionHasError, maxLength: 1000, minLines: null, maxLines: null, @@ -124,6 +105,10 @@ class _OptionWidgetState extends State { }, ), ), + if (state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].descriptionHasError) ...[ + const SizedBox(height: 4), + ValidationErrorWidget(error: s.atLeast3Symbols), + ], const SizedBox(height: 12), Row( children: [ @@ -141,14 +126,8 @@ class _OptionWidgetState extends State { const SizedBox(height: 4), CustomTextFormField( controller: creatorPriceController, - onChanged: (value) { - double? price = double.tryParse(value.replaceAll('₽', '')); - if (price != null) { - price = price * 1.2; - customerPriceController.text = price.toStringAsFixed(2); - context.read().setPrice(price); - } - }, + readOnly: true, + textStyle: TextStyle(color: appTheme.gray), maxLength: 20, ), const SizedBox(height: 12), @@ -169,10 +148,11 @@ class _OptionWidgetState extends State { CustomTextFormField( controller: customerPriceController, textInputType: const TextInputType.numberWithOptions(decimal: true), + hasError: state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].priceHasError, onChanged: (value) { final price = double.tryParse(value.replaceAll('₽', '')); + creatorPriceController.text = '${((price ?? 0) * 0.8).toStringAsFixed(2)}₽'; if (price != null) { - creatorPriceController.text = (price * 0.8).toStringAsFixed(2); context.read().setPrice(price); } }, @@ -235,33 +215,33 @@ class _OptionWidgetState extends State { ), const SizedBox(height: 16), const CustomDivider(), - const SizedBox(height: 16), - Row( - children: [ - Checkbox( - value: isChecked, - activeColor: appTheme.gray, - checkColor: appTheme.gray, - fillColor: const WidgetStatePropertyAll(Colors.transparent), - side: WidgetStateBorderSide.resolveWith( - (Set states) { - return BorderSide(width: 1, color: appTheme.gray); - }, - ), - onChanged: (bool? value) { - setState(() { - isChecked = value ?? false; - }); - }, - ), - Expanded( - child: Text( - s.option_personal_diet_desc, - style: TextStyles.textMMedium.copyWith(color: appTheme.black), - ), - ), - ], - ), + // const SizedBox(height: 16), + // Row( + // children: [ + // Checkbox( + // value: isChecked, + // activeColor: appTheme.gray, + // checkColor: appTheme.gray, + // fillColor: const WidgetStatePropertyAll(Colors.transparent), + // side: WidgetStateBorderSide.resolveWith( + // (Set states) { + // return BorderSide(width: 1, color: appTheme.gray); + // }, + // ), + // onChanged: (bool? value) { + // setState(() { + // isChecked = value ?? false; + // }); + // }, + // ), + // Expanded( + // child: Text( + // s.option_personal_diet_desc, + // style: TextStyles.textMMedium.copyWith(color: appTheme.black), + // ), + // ), + // ], + // ), ], ), ); diff --git a/lib/features/create_plan/presentation/widgets/plan_duration_drop_down_widget.dart b/lib/features/create_plan/presentation/widgets/plan_duration_drop_down_widget.dart index 9e06a8e7..ad4100c0 100644 --- a/lib/features/create_plan/presentation/widgets/plan_duration_drop_down_widget.dart +++ b/lib/features/create_plan/presentation/widgets/plan_duration_drop_down_widget.dart @@ -3,6 +3,7 @@ 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/widgets/custom_drop_down_button.dart'; +import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart'; import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart'; import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart'; import 'package:eatvio_app/generated/l10n.dart'; @@ -36,28 +37,37 @@ class _PlanDurationDropDownWidgetState extends State final s = S.of(context); return BlocBuilder( builder: (context, state) { - return CustomDropDownButton( - hint: Text( - s.choose_plan_duration, - style: TextStyles.textMMedium.copyWith(color: appTheme.black), - ), - onChanged: (value) { - if (value != null) { - context.read().setDuration(durationDropDownTitles.indexOf(value)); - } - }, - items: durationDropDownTitles - .mapIndexed( - (index, value) => DropdownMenuItem( - value: durationDropDownTitles[index], - child: Text( - durationDropDownTitles[index], - style: TextStyles.textMMedium.copyWith(color: appTheme.black), - ), - ), - ) - .toList(), - value: state.durationIndex == null ? null : durationDropDownTitles[state.durationIndex ?? 0], + return Column( + children: [ + CustomDropDownButton( + hasError: state.validationState.durationHasError, + hint: Text( + s.choose_plan_duration, + style: TextStyles.textMMedium.copyWith(color: appTheme.black), + ), + onChanged: (value) { + if (value != null) { + context.read().setDuration(durationDropDownTitles.indexOf(value)); + } + }, + items: durationDropDownTitles + .mapIndexed( + (index, value) => DropdownMenuItem( + value: durationDropDownTitles[index], + child: Text( + durationDropDownTitles[index], + style: TextStyles.textMMedium.copyWith(color: appTheme.black), + ), + ), + ) + .toList(), + value: state.durationIndex == null ? null : durationDropDownTitles[state.durationIndex ?? 0], + ), + if (state.validationState.durationHasError) ...[ + const SizedBox(height: 4), + ValidationErrorWidget(error: s.choose_plan_duration), + ], + ], ); }, ); diff --git a/lib/features/create_plan/presentation/widgets/plan_image_picker_widget.dart b/lib/features/create_plan/presentation/widgets/plan_image_picker_widget.dart index c834d5a7..0e744a37 100644 --- a/lib/features/create_plan/presentation/widgets/plan_image_picker_widget.dart +++ b/lib/features/create_plan/presentation/widgets/plan_image_picker_widget.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:dotted_border/dotted_border.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/create_diet/presentation/widgets/error_widget.dart'; import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart'; import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart'; import 'package:eatvio_app/generated/l10n.dart'; @@ -72,74 +73,79 @@ class _PlanImagePickerWidgetState extends State { final s = S.of(context); return BlocBuilder( builder: (context, state) { - return GestureDetector( - onTap: () { - _showImagePickerOptions(s, context.read()); - }, - child: Container( - height: 240, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8), - image: state.planData.image != null ? DecorationImage(fit: BoxFit.cover, image: FileImage(File(state.planData.image?.path ?? ''))) : null, - color: appTheme.gray50, + return Column( + children: [ + GestureDetector( + onTap: () { + _showImagePickerOptions(s, context.read()); + }, + child: Container( + height: 240, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + image: state.planData.image != null ? DecorationImage(fit: BoxFit.cover, image: FileImage(File(state.planData.image?.path ?? ''))) : null, + color: appTheme.gray50, + ), + width: MediaQuery.sizeOf(context).width, + padding: state.planData.image != null ? null : const EdgeInsets.all(8), + child: state.planData.image != null + ? Padding( + padding: const EdgeInsets.only(top: 6, right: 6), + child: GestureDetector( + onTap: () { + context.read().setImage(null); + }, + child: Align( + alignment: Alignment.topRight, + child: Container( + width: 34, + height: 34, + decoration: BoxDecoration( + color: appTheme.dividerColor, + shape: BoxShape.circle, + ), + child: const Icon( + Icons.close_rounded, + color: Colors.grey, + size: 16, + ), + ), + ), + ), + ) + : Stack( + children: [ + DottedBorder( + stackFit: StackFit.expand, + dashPattern: const [14, 14], + color: state.validationState.imageHasError ? appTheme.red : appTheme.dottedBorder, + borderType: BorderType.RRect, + radius: const Radius.circular(12), + child: const SizedBox.expand(), + ), + Center( + child: Column( + children: [ + const SizedBox( + height: 48, + ), + SvgPicture.asset('assets/icons/eatvio_logo.svg'), + const SizedBox( + height: 24, + ), + Text( + s.add_cover, + style: TextStyles.textMMedium.copyWith(color: appTheme.gray), + ), + ], + ), + ), + ], + ), + ), ), - width: MediaQuery.sizeOf(context).width, - padding: state.planData.image != null ? null : const EdgeInsets.all(8), - child: state.planData.image != null - ? Padding( - padding: const EdgeInsets.only(top: 6, right: 6), - child: GestureDetector( - onTap: () { - context.read().setImage(null); - }, - child: Align( - alignment: Alignment.topRight, - child: Container( - width: 34, - height: 34, - decoration: BoxDecoration( - color: appTheme.dividerColor, - shape: BoxShape.circle, - ), - child: const Icon( - Icons.close_rounded, - color: Colors.grey, - size: 16, - ), - ), - ), - ), - ) - : Stack( - children: [ - DottedBorder( - stackFit: StackFit.expand, - dashPattern: const [14, 14], - color: appTheme.dottedBorder, - borderType: BorderType.RRect, - radius: const Radius.circular(12), - child: const SizedBox.expand(), - ), - Center( - child: Column( - children: [ - const SizedBox( - height: 48, - ), - SvgPicture.asset('assets/icons/eatvio_logo.svg'), - const SizedBox( - height: 24, - ), - Text( - s.add_cover, - style: TextStyles.textMMedium.copyWith(color: appTheme.gray), - ), - ], - ), - ), - ], - ), - ), + if (state.validationState.imageHasError) ...[const SizedBox(height: 8), ValidationErrorWidget(error: s.add_cover)], + ], ); }, ); diff --git a/lib/features/create_plan/presentation/widgets/plan_type_choice_widget.dart b/lib/features/create_plan/presentation/widgets/plan_type_choice_widget.dart index cff69d5e..a50d461d 100644 --- a/lib/features/create_plan/presentation/widgets/plan_type_choice_widget.dart +++ b/lib/features/create_plan/presentation/widgets/plan_type_choice_widget.dart @@ -1,5 +1,6 @@ import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart'; import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart'; import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart'; import 'package:eatvio_app/generated/l10n.dart'; @@ -50,49 +51,55 @@ class _PlanTypeChoiceWidgetState extends State { @override Widget build(BuildContext context) { + final s = S.of(context); return BlocBuilder( builder: (context, state) { - return Row( + return Column( children: [ - ...List.generate(4, (index) { - return Expanded( - child: GestureDetector( - onTap: () { - final planTypes = [...state.planData.type]; - planTypes[index] = !planTypes[index]; - context.read().setPlanType(planTypes); - }, - child: Container( - margin: EdgeInsets.only(right: index == 3 ? 0 : 4), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - color: state.planData.type[index] ? appTheme.cardBackground : appTheme.whiteA700, - border: Border.all( - width: 1, - color: state.planData.type[index] ? appTheme.blueA400 : appTheme.lightGray00, + Row( + children: [ + ...List.generate(4, (index) { + return Expanded( + child: GestureDetector( + onTap: () { + final planTypes = [...state.planData.type]; + planTypes[index] = !planTypes[index]; + context.read().setPlanType(planTypes); + }, + child: Container( + margin: EdgeInsets.only(right: index == 3 ? 0 : 4), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: state.planData.type[index] ? appTheme.cardBackground : appTheme.whiteA700, + border: Border.all( + width: 1, + color: state.planData.type[index] ? appTheme.blueA400 : appTheme.lightGray00, + ), + ), + height: 104, + child: Column( + children: [ + const SizedBox(height: 16), + Image.asset( + choiceContent[index].$1, + height: 30, + ), + const SizedBox(height: 8), + Text( + choiceContent[index].$2, + overflow: TextOverflow.clip, + textAlign: TextAlign.center, + style: TextStyles.textFS10FW500.copyWith(color: appTheme.black), + ), + ], + ), ), ), - height: 104, - child: Column( - children: [ - const SizedBox(height: 16), - Image.asset( - choiceContent[index].$1, - height: 30, - ), - const SizedBox(height: 8), - Text( - choiceContent[index].$2, - overflow: TextOverflow.clip, - textAlign: TextAlign.center, - style: TextStyles.textFS10FW500.copyWith(color: appTheme.black), - ), - ], - ), - ), - ), - ); - }), + ); + }), + ], + ), + if (state.validationState.typeHasError) ...[const SizedBox(height: 8), ValidationErrorWidget(error: s.chosePlanType)] ], ); }, diff --git a/lib/features/create_plan/repositories/create_plan_repository.dart b/lib/features/create_plan/repositories/create_plan_repository.dart index 0f01edbc..09a4fcb6 100644 --- a/lib/features/create_plan/repositories/create_plan_repository.dart +++ b/lib/features/create_plan/repositories/create_plan_repository.dart @@ -1,3 +1,5 @@ +import 'package:dio/dio.dart'; +import 'package:eatvio_app/features/create_plan/models/plan_slug_model.dart'; import 'package:eatvio_app/features/create_plan/services/create_plan_service.dart'; class CreatePlanRepository { @@ -5,10 +7,11 @@ class CreatePlanRepository { CreatePlanRepository({required this.createPlanService}); - Future createPlan({ + Future createPlan({ required Map model, }) async { - createPlanService.createPlan(model: model); + final formData = FormData.fromMap(model); + return await createPlanService.createPlan(formData: formData); } Future createPacketOption({ diff --git a/lib/features/create_plan/services/create_plan_service.dart b/lib/features/create_plan/services/create_plan_service.dart index f5d1891e..513d39b4 100644 --- a/lib/features/create_plan/services/create_plan_service.dart +++ b/lib/features/create_plan/services/create_plan_service.dart @@ -1,4 +1,5 @@ import 'package:dio/dio.dart'; +import 'package:eatvio_app/features/create_plan/models/plan_slug_model.dart'; import 'package:retrofit/retrofit.dart'; part 'create_plan_service.g.dart'; @@ -9,8 +10,8 @@ abstract class CreatePlanService { @POST('/api/plans/create') @MultiPart() - Future createPlan({ - @Body() required Map model, + Future createPlan({ + @Body() required FormData formData, }); @POST('/api/plans/create/{slug}/packet_option') @@ -19,7 +20,7 @@ abstract class CreatePlanService { @Body() required Map model, }); - @POST('/api/plans/create/{slug}/packet_options') + @POST('/api/plans/create/{slug}/packet_options') Future createPacketOptions({ @Path() required String slug, @Body() required Map model, diff --git a/lib/features/diary/models/consultant_review_model.dart b/lib/features/diary/models/consultant_review_model.dart index 95b12813..f31b21c9 100644 --- a/lib/features/diary/models/consultant_review_model.dart +++ b/lib/features/diary/models/consultant_review_model.dart @@ -36,6 +36,7 @@ class MealComment with _$MealComment { factory MealComment({ required String comment, + required int typeNo, }) = _MealComment; factory MealComment.fromJson(Map json) => _$MealCommentFromJson(json); @@ -45,7 +46,7 @@ class MealComment with _$MealComment { class PortionComment with _$PortionComment { factory PortionComment({ - @JsonKey(name: 'commentable_id') required int portionId, + @JsonKey(name: 'id') required int portionId, required String comment, }) = _PortionComment; diff --git a/lib/features/diary/models/consultant_review_model.freezed.dart b/lib/features/diary/models/consultant_review_model.freezed.dart index 96310b65..44d803d4 100644 --- a/lib/features/diary/models/consultant_review_model.freezed.dart +++ b/lib/features/diary/models/consultant_review_model.freezed.dart @@ -479,6 +479,7 @@ MealComment _$MealCommentFromJson(Map json) { /// @nodoc mixin _$MealComment { String get comment => throw _privateConstructorUsedError; + int get typeNo => throw _privateConstructorUsedError; /// Serializes this MealComment to a JSON map. Map toJson() => throw _privateConstructorUsedError; @@ -496,7 +497,7 @@ abstract class $MealCommentCopyWith<$Res> { MealComment value, $Res Function(MealComment) then) = _$MealCommentCopyWithImpl<$Res, MealComment>; @useResult - $Res call({String comment}); + $Res call({String comment, int typeNo}); } /// @nodoc @@ -515,12 +516,17 @@ class _$MealCommentCopyWithImpl<$Res, $Val extends MealComment> @override $Res call({ Object? comment = null, + Object? typeNo = null, }) { return _then(_value.copyWith( comment: null == comment ? _value.comment : comment // ignore: cast_nullable_to_non_nullable as String, + typeNo: null == typeNo + ? _value.typeNo + : typeNo // ignore: cast_nullable_to_non_nullable + as int, ) as $Val); } } @@ -533,7 +539,7 @@ abstract class _$$MealCommentImplCopyWith<$Res> __$$MealCommentImplCopyWithImpl<$Res>; @override @useResult - $Res call({String comment}); + $Res call({String comment, int typeNo}); } /// @nodoc @@ -550,12 +556,17 @@ class __$$MealCommentImplCopyWithImpl<$Res> @override $Res call({ Object? comment = null, + Object? typeNo = null, }) { return _then(_$MealCommentImpl( comment: null == comment ? _value.comment : comment // ignore: cast_nullable_to_non_nullable as String, + typeNo: null == typeNo + ? _value.typeNo + : typeNo // ignore: cast_nullable_to_non_nullable + as int, )); } } @@ -563,17 +574,19 @@ class __$$MealCommentImplCopyWithImpl<$Res> /// @nodoc @JsonSerializable() class _$MealCommentImpl implements _MealComment { - _$MealCommentImpl({required this.comment}); + _$MealCommentImpl({required this.comment, required this.typeNo}); factory _$MealCommentImpl.fromJson(Map json) => _$$MealCommentImplFromJson(json); @override final String comment; + @override + final int typeNo; @override String toString() { - return 'MealComment(comment: $comment)'; + return 'MealComment(comment: $comment, typeNo: $typeNo)'; } @override @@ -581,12 +594,13 @@ class _$MealCommentImpl implements _MealComment { return identical(this, other) || (other.runtimeType == runtimeType && other is _$MealCommentImpl && - (identical(other.comment, comment) || other.comment == comment)); + (identical(other.comment, comment) || other.comment == comment) && + (identical(other.typeNo, typeNo) || other.typeNo == typeNo)); } @JsonKey(includeFromJson: false, includeToJson: false) @override - int get hashCode => Object.hash(runtimeType, comment); + int get hashCode => Object.hash(runtimeType, comment, typeNo); /// Create a copy of MealComment /// with the given fields replaced by the non-null parameter values. @@ -605,13 +619,17 @@ class _$MealCommentImpl implements _MealComment { } abstract class _MealComment implements MealComment { - factory _MealComment({required final String comment}) = _$MealCommentImpl; + factory _MealComment( + {required final String comment, + required final int typeNo}) = _$MealCommentImpl; factory _MealComment.fromJson(Map json) = _$MealCommentImpl.fromJson; @override String get comment; + @override + int get typeNo; /// Create a copy of MealComment /// with the given fields replaced by the non-null parameter values. @@ -627,7 +645,7 @@ PortionComment _$PortionCommentFromJson(Map json) { /// @nodoc mixin _$PortionComment { - @JsonKey(name: 'commentable_id') + @JsonKey(name: 'id') int get portionId => throw _privateConstructorUsedError; String get comment => throw _privateConstructorUsedError; @@ -647,7 +665,7 @@ abstract class $PortionCommentCopyWith<$Res> { PortionComment value, $Res Function(PortionComment) then) = _$PortionCommentCopyWithImpl<$Res, PortionComment>; @useResult - $Res call({@JsonKey(name: 'commentable_id') int portionId, String comment}); + $Res call({@JsonKey(name: 'id') int portionId, String comment}); } /// @nodoc @@ -689,7 +707,7 @@ abstract class _$$PortionCommentImplCopyWith<$Res> __$$PortionCommentImplCopyWithImpl<$Res>; @override @useResult - $Res call({@JsonKey(name: 'commentable_id') int portionId, String comment}); + $Res call({@JsonKey(name: 'id') int portionId, String comment}); } /// @nodoc @@ -725,14 +743,13 @@ class __$$PortionCommentImplCopyWithImpl<$Res> @JsonSerializable() class _$PortionCommentImpl implements _PortionComment { _$PortionCommentImpl( - {@JsonKey(name: 'commentable_id') required this.portionId, - required this.comment}); + {@JsonKey(name: 'id') required this.portionId, required this.comment}); factory _$PortionCommentImpl.fromJson(Map json) => _$$PortionCommentImplFromJson(json); @override - @JsonKey(name: 'commentable_id') + @JsonKey(name: 'id') final int portionId; @override final String comment; @@ -775,14 +792,14 @@ class _$PortionCommentImpl implements _PortionComment { abstract class _PortionComment implements PortionComment { factory _PortionComment( - {@JsonKey(name: 'commentable_id') required final int portionId, + {@JsonKey(name: 'id') required final int portionId, required final String comment}) = _$PortionCommentImpl; factory _PortionComment.fromJson(Map json) = _$PortionCommentImpl.fromJson; @override - @JsonKey(name: 'commentable_id') + @JsonKey(name: 'id') int get portionId; @override String get comment; diff --git a/lib/features/diary/models/consultant_review_model.g.dart b/lib/features/diary/models/consultant_review_model.g.dart index f08faeb1..fd223d76 100644 --- a/lib/features/diary/models/consultant_review_model.g.dart +++ b/lib/features/diary/models/consultant_review_model.g.dart @@ -48,22 +48,24 @@ Map _$$ConsultantCommentsImplToJson( _$MealCommentImpl _$$MealCommentImplFromJson(Map json) => _$MealCommentImpl( comment: json['comment'] as String, + typeNo: (json['typeNo'] as num).toInt(), ); Map _$$MealCommentImplToJson(_$MealCommentImpl instance) => { 'comment': instance.comment, + 'typeNo': instance.typeNo, }; _$PortionCommentImpl _$$PortionCommentImplFromJson(Map json) => _$PortionCommentImpl( - portionId: (json['commentable_id'] as num).toInt(), + portionId: (json['id'] as num).toInt(), comment: json['comment'] as String, ); Map _$$PortionCommentImplToJson( _$PortionCommentImpl instance) => { - 'commentable_id': instance.portionId, + 'id': instance.portionId, 'comment': instance.comment, }; diff --git a/lib/features/diary/presentation/widgets/meal_button_widget.dart b/lib/features/diary/presentation/widgets/meal_button_widget.dart index 6ac80f20..afeef711 100644 --- a/lib/features/diary/presentation/widgets/meal_button_widget.dart +++ b/lib/features/diary/presentation/widgets/meal_button_widget.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.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'; @@ -6,7 +7,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; class MealButtonWidget extends StatefulWidget { final int mealNumber; - const MealButtonWidget({ super.key, required this.mealNumber, @@ -22,6 +22,18 @@ class _MealButtonWidgetState extends State { return BlocBuilder( builder: (context, state) { if (state is DiaryStateLoaded) { + final consultantComment = state.consultantReview?.consultantComments.mealsComments.firstWhereOrNull( + (element) => element.typeNo == widget.mealNumber, + ); + + final Color bgColor; + if (state.selectedMeal == widget.mealNumber) { + bgColor = appTheme.blueA400; + } else if (consultantComment != null) { + bgColor = appTheme.commentBg; + } else { + bgColor = const Color(0xFFF1F2F6); + } return InkWell( onTap: () { context.read().setSelectedMeal(widget.mealNumber); @@ -32,7 +44,7 @@ class _MealButtonWidgetState extends State { height: 32, decoration: BoxDecoration( borderRadius: BorderRadius.circular(100), - color: state.selectedMeal == widget.mealNumber ? appTheme.blueA400 : const Color(0xFFF1F2F6), + color: bgColor, ), child: Center( child: Text( diff --git a/lib/features/diary/presentation/widgets/meal_widget.dart b/lib/features/diary/presentation/widgets/meal_widget.dart index 591d1a65..93f9fb2d 100644 --- a/lib/features/diary/presentation/widgets/meal_widget.dart +++ b/lib/features/diary/presentation/widgets/meal_widget.dart @@ -1,3 +1,4 @@ +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/features/diary/presentation/bloc/diary_cubit.dart'; @@ -81,8 +82,10 @@ class _MealWidgetState extends State { BlocBuilder( builder: (context, state) { if (state is DiaryStateLoaded) { - final mealsComments = state.consultantReview?.consultantComments.mealsComments; - if (mealsComments != null && (state.selectedMeal - 1 < mealsComments.length)) { + final consultantCommentOfMeal = state.consultantReview?.consultantComments.mealsComments.firstWhereOrNull( + (element) => element.typeNo == state.selectedMeal, + ); + if (consultantCommentOfMeal != null) { return Column( children: [ Text( @@ -90,7 +93,7 @@ class _MealWidgetState extends State { style: TextStyles.textLSemibold.copyWith(color: appTheme.black), ), const SizedBox(height: 12), - CommentContainer(comment: mealsComments[state.selectedMeal - 1].comment), + CommentContainer(comment: consultantCommentOfMeal.comment), ], ); } diff --git a/lib/features/diary/repositories/diary_repository.dart b/lib/features/diary/repositories/diary_repository.dart index 5a9df1da..880e0df1 100644 --- a/lib/features/diary/repositories/diary_repository.dart +++ b/lib/features/diary/repositories/diary_repository.dart @@ -11,7 +11,7 @@ import 'package:eatvio_app/features/glossary/models/glossary_model.dart'; class DiaryRepository { final DiaryService diaryService; - final StreamController _diaryForceFetchController = StreamController(); + final StreamController _diaryForceFetchController = StreamController.broadcast(); Stream get forceFetchStream => _diaryForceFetchController.stream; diff --git a/lib/features/feed/models/plan_pagination_model.dart b/lib/features/feed/models/plan_pagination_model.dart index b835effc..63e283c6 100644 --- a/lib/features/feed/models/plan_pagination_model.dart +++ b/lib/features/feed/models/plan_pagination_model.dart @@ -1,5 +1,6 @@ import 'package:eatvio_app/core/models/pagination_model.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; +import 'package:eatvio_app/features/my_finance/models/card_model.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'plan_pagination_model.freezed.dart'; @@ -70,10 +71,10 @@ class PacketOptions with _$PacketOptions { class PlanType with _$PlanType { const PlanType._(); const factory PlanType({ - @JsonKey(name: "fit") required bool? fit, - @JsonKey(name: "slim") required bool? slim, - @JsonKey(name: "behavior") required bool? behavior, - @JsonKey(name: "wellness") required bool? wellness, + @JsonKey(name: "fit", fromJson: parseBool) required bool? fit, + @JsonKey(name: "slim", fromJson: parseBool) required bool? slim, + @JsonKey(name: "behavior", fromJson: parseBool) required bool? behavior, + @JsonKey(name: "wellness", fromJson: parseBool) required bool? wellness, }) = _PlanType; factory PlanType.fromJson(Map json) => _$PlanTypeFromJson(json); diff --git a/lib/features/my_diets/presentation/widgets/my_diet_widget.dart b/lib/features/my_diets/presentation/widgets/my_diet_widget.dart index e2a687fb..cfbd39d5 100644 --- a/lib/features/my_diets/presentation/widgets/my_diet_widget.dart +++ b/lib/features/my_diets/presentation/widgets/my_diet_widget.dart @@ -118,7 +118,7 @@ class _MyDietWidgetState extends State { if (!context.mounted) return; showBottomSheetWithBlur( context: context, - child: SuccessPublishedDietBottomSheet(title: widget.diet.name), + child: SuccessPublishedDietOrPlanBottomSheet(title: widget.diet.name), ); await context.read().loadAllDiets(); } else { diff --git a/lib/features/my_finance/models/card_model.dart b/lib/features/my_finance/models/card_model.dart index c923519a..a6f0b159 100644 --- a/lib/features/my_finance/models/card_model.dart +++ b/lib/features/my_finance/models/card_model.dart @@ -5,7 +5,6 @@ part 'card_model.g.dart'; @freezed class CardModel with _$CardModel { - factory CardModel({ required int id, @JsonKey(name: 'bank_pan') required String cardNumber, @@ -16,7 +15,7 @@ class CardModel with _$CardModel { } bool parseBool(json) { - if (json == 1 || json == true) { + if (json == 1 || json == true || json == '1') { return true; } return false; diff --git a/lib/features/my_finance/presentation/cubit/user_cards_cubit/user_cards_cubit.dart b/lib/features/my_finance/presentation/cubit/user_cards_cubit/user_cards_cubit.dart index c35b8ed1..184cd44f 100644 --- a/lib/features/my_finance/presentation/cubit/user_cards_cubit/user_cards_cubit.dart +++ b/lib/features/my_finance/presentation/cubit/user_cards_cubit/user_cards_cubit.dart @@ -28,20 +28,19 @@ class UserCardsCubit extends Cubit { emit(state.copyWith(cards: cards, state: BaseState.success)); } catch (e, st) { log(e.toString(), stackTrace: st); - emit(state.copyWith(state: BaseState.error, error: e.toString())); + emit(state.copyWith(state: BaseState.error, error: e.toString())); } } Future markAsDefault({required int id}) async { final oldCards = [...state.cards]; if (isClosed) return; - final cards = - oldCards.map((card) { - if (card.id == id) { - return card.copyWith(isDefault: true); - } - return card.copyWith(isDefault: false); - }).toList(); + final cards = oldCards.map((card) { + if (card.id == id) { + return card.copyWith(isDefault: true); + } + return card.copyWith(isDefault: false); + }).toList(); emit(state.copyWith(cards: cards)); EasyDebounce.debounce('markAsDefault', Duration(milliseconds: id), () async { @@ -66,4 +65,14 @@ class UserCardsCubit extends Cubit { log(e.toString(), stackTrace: st); } } + + Future collectCharge({required int chosenPlaId}) async { + try { + await cardsRepository.collectCharge(chosenPlaId: chosenPlaId); + return true; + } catch (e, st) { + log(e.toString(), stackTrace: st); + } + return false; + } } diff --git a/lib/features/my_finance/presentation/pages/card_settings.dart b/lib/features/my_finance/presentation/pages/card_settings.dart index 5d690995..fa271da6 100644 --- a/lib/features/my_finance/presentation/pages/card_settings.dart +++ b/lib/features/my_finance/presentation/pages/card_settings.dart @@ -1,7 +1,11 @@ +import 'package:eatvio_app/core/app_router.dart'; import 'package:eatvio_app/core/enums/base_state.dart'; 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/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/data_error_widget.dart'; import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/features/my_finance/presentation/cubit/user_cards_cubit/user_cards_cubit.dart'; @@ -11,104 +15,221 @@ import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:collection/collection.dart'; +import 'package:go_router/go_router.dart'; class CardSettingsScreen extends StatelessWidget { static const path = '/card-settings-screen'; static const name = 'cardSettingsScreen'; - const CardSettingsScreen({super.key}); - + const CardSettingsScreen({super.key, this.chosenPlanId}); + final int? chosenPlanId; @override Widget build(BuildContext context) { - return BlocProvider(create: (context) => sl()..getCards(), child: const CardsSettingsContent()); + return BlocProvider( + create: (context) => sl()..getCards(), + child: CardsSettingsContent( + chosenPlanId: chosenPlanId, + )); } } class CardsSettingsContent extends StatelessWidget { - const CardsSettingsContent({super.key}); + const CardsSettingsContent({super.key, this.chosenPlanId}); + final int? chosenPlanId; @override Widget build(BuildContext context) { final s = S.of(context); - return Scaffold( - backgroundColor: const Color(0xFFF0F2F5), - appBar: AppBar( - shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(bottom: Radius.circular(12))), - title: Text(s.my_cards), - centerTitle: true, - ), - body: Column( - children: [ - const SizedBox(height: 16), - Container( - decoration: BoxDecoration(borderRadius: BorderRadius.circular(12), color: Colors.white), - width: double.infinity, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 16), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 16), - child: Text('Привязанные карты', style: TextStyle(color: Color(0xFF323F4B), fontSize: 18, fontWeight: FontWeight.w600, height: 1)), + return BlocBuilder( + builder: (context, state) { + return Scaffold( + backgroundColor: const Color(0xFFF0F2F5), + appBar: AppBar( + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(bottom: Radius.circular(12))), + title: Text(s.my_cards), + centerTitle: true, + ), + body: Column( + children: [ + const SizedBox(height: 16), + Container( + decoration: BoxDecoration(borderRadius: BorderRadius.circular(12), color: Colors.white), + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), + child: Text('Привязанные карты', style: TextStyle(color: Color(0xFF323F4B), fontSize: 18, fontWeight: FontWeight.w600, height: 1)), + ), + const SizedBox(height: 8), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Builder( + builder: (context) { + final defaultCardNumber = state.cards.firstWhereOrNull((element) => element.isDefault)?.cardNumber; + if (defaultCardNumber != null) { + return Text( + 'Выплату будут производиться\nна карту ${defaultCardNumber.substring(defaultCardNumber.length - 7)}', + style: const TextStyle(color: Color(0xFF323F4B), fontSize: 16, fontWeight: FontWeight.w500, height: 1), + ); + } + return Text(s.no_default_cards, style: TextStyles.textMMedium.copyWith(color: appTheme.black)); + }, + ), + ), + const SizedBox(height: 16), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Builder( + builder: (context) { + switch (state.state) { + case BaseState.error: + return SizedBox( + width: MediaQuery.sizeOf(context).width, + child: Center( + child: DataErrorWidget( + onReload: () async { + context.read().getCards(); + }, + ), + ), + ); + case BaseState.success: + return Padding( + padding: const EdgeInsets.only(left: 12), + child: Row( + spacing: 12, + children: List.generate(state.cards.length + 1, (index) { + if (index < state.cards.length) { + final cardsNumber = state.cards[index].cardNumber.substring(state.cards[index].cardNumber.length - 7); + return CardWidget(isSelected: state.cards[index].isDefault, cardId: state.cards[index].id, cardNumber: cardsNumber); + } + return const CardWidget(enabledAddCard: true); + }), + ), + ); + + default: + return SizedBox(width: MediaQuery.sizeOf(context).width, height: 200, child: const Center(child: Loader())); + } + }, + ), + ), + const SizedBox(height: 16), + ], ), - const SizedBox(height: 8), + ), + if (chosenPlanId != null && state.cards.isNotEmpty) ...[ Padding( padding: const EdgeInsets.symmetric(horizontal: 16), - child: BlocBuilder( - builder: (context, state) { + child: CustomOutlinedButton( + text: s.collectCharge, + height: 52, + buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), + buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.green)), + onPressed: () async { final defaultCardNumber = state.cards.firstWhereOrNull((element) => element.isDefault)?.cardNumber; - if (defaultCardNumber != null) { - return Text( - 'Выплату будут производиться\nна карту ${defaultCardNumber.substring(defaultCardNumber.length - 7)}', - style: const TextStyle(color: Color(0xFF323F4B), fontSize: 16, fontWeight: FontWeight.w500, height: 1), + final result = await context.read().collectCharge(chosenPlaId: chosenPlanId!); + if (context.mounted) { + if (!result) { + showBottomSheetWithBlur( + context: context, + child: Column( + children: [ + const SizedBox( + height: 8, + ), + const BottomSheetHandlerBar(), + const SizedBox( + height: 20, + ), + Text( + s.chargeCollectionFailed, + style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), + ), + const SizedBox( + height: 8, + ), + Text( + s.contactSupport, + style: TextStyles.textMMediumAuto.copyWith(color: appTheme.black), + ), + const SizedBox( + height: 20, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: CustomOutlinedButton( + onPressed: () { + AppRouter.navigatorKey.currentContext?.pop(); + }, + text: s.ok, + height: 52, + buttonTextStyle: TextStyles.textSSemibold.copyWith(color: appTheme.whiteA700), + buttonStyle: ButtonStyle( + side: const WidgetStatePropertyAll(BorderSide.none), + backgroundColor: WidgetStatePropertyAll(appTheme.blueA400), + ), + ), + ), + ], + ), + ); + return; + } + showBottomSheetWithBlur( + context: context, + child: Column( + children: [ + const SizedBox( + height: 8, + ), + const BottomSheetHandlerBar(), + const SizedBox( + height: 20, + ), + Text( + s.chargeApproved, + style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), + ), + const SizedBox( + height: 8, + ), + Text( + '${s.moneySentToCard} ${defaultCardNumber?.substring(defaultCardNumber.length - 7)}', + textAlign: TextAlign.center, + style: TextStyles.textMMediumAuto.copyWith(color: appTheme.black), + ), + const SizedBox( + height: 8, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: CustomOutlinedButton( + onPressed: () { + AppRouter.navigatorKey.currentContext?.pop(); + }, + text: s.ok, + height: 52, + buttonTextStyle: TextStyles.textSSemibold.copyWith(color: appTheme.whiteA700), + buttonStyle: ButtonStyle( + side: const WidgetStatePropertyAll(BorderSide.none), + backgroundColor: WidgetStatePropertyAll(appTheme.blueA400), + ), + ), + ), + ], + ), ); } - return Text(s.no_default_cards, style: TextStyles.textMMedium.copyWith(color: appTheme.black)); }, ), - ), - const SizedBox(height: 16), - SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: BlocBuilder( - builder: (context, state) { - switch (state.state) { - case BaseState.error: - return SizedBox( - width: MediaQuery.sizeOf(context).width, - child: Center( - child: DataErrorWidget( - onReload: () async { - context.read().getCards(); - }, - ), - ), - ); - case BaseState.success: - return Padding( - padding: const EdgeInsets.only(left: 12), - child: Row( - spacing: 12, - children: List.generate(state.cards.length + 1, (index) { - if (index < state.cards.length) { - final cardsNumber = state.cards[index].cardNumber.substring(state.cards[index].cardNumber.length - 7); - return CardWidget(isSelected: state.cards[index].isDefault, cardId: state.cards[index].id, cardNumber: cardsNumber); - } - return const CardWidget(enabledAddCard: true); - }), - ), - ); - - default: - return SizedBox(width: MediaQuery.sizeOf(context).width, height: 200, child: const Center(child: Loader())); - } - }, - ), - ), - const SizedBox(height: 16), - ], - ), + ) + ] + ], ), - ], - ), + ); + }, ); } } diff --git a/lib/features/my_finance/repositories/cards_repository.dart b/lib/features/my_finance/repositories/cards_repository.dart index 221a3376..42534348 100644 --- a/lib/features/my_finance/repositories/cards_repository.dart +++ b/lib/features/my_finance/repositories/cards_repository.dart @@ -36,4 +36,12 @@ class CardsRepository { rethrow; } } + + Future collectCharge({required int chosenPlaId}) async { + try { + await cardsService.collectCharge(chosenPlanId: chosenPlaId); + } catch (e) { + rethrow; + } + } } diff --git a/lib/features/my_finance/services/cards_service.dart b/lib/features/my_finance/services/cards_service.dart index b42a8ed7..c653d1a5 100644 --- a/lib/features/my_finance/services/cards_service.dart +++ b/lib/features/my_finance/services/cards_service.dart @@ -24,5 +24,10 @@ abstract class CardsService { Future deleteCard({ @Path('id') required int id, }); + + @POST('/api/chosen_plans/{id}/take_payin') + Future collectCharge({ + @Path('id') required int chosenPlanId, + }); } 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 2198b589..a8168917 100644 --- a/lib/features/order_info/presentation/info_by_order_screen.dart +++ b/lib/features/order_info/presentation/info_by_order_screen.dart @@ -8,6 +8,7 @@ import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart' import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart'; import 'package:eatvio_app/features/order_info/cubit/order_info_cubit.dart'; import 'package:eatvio_app/features/order_info/cubit/order_info_state.dart'; +import 'package:eatvio_app/features/order_info/presentation/widgets/collect_charge_banner.dart'; import 'package:eatvio_app/features/order_info/presentation/widgets/payment_banner.dart'; import 'package:eatvio_app/features/order_info/presentation/widgets/plan_info.dart'; import 'package:eatvio_app/features/order_info/presentation/widgets/plan_props.dart'; @@ -61,7 +62,7 @@ class _InfoByOrderScreenContentState extends State { builder: (context, state) { final plan = state.chosenPlan; if (state.isLoading) { - Scaffold( + return Scaffold( appBar: AppBar( title: Text( s.infoAboutOrder, @@ -113,7 +114,7 @@ class _InfoByOrderScreenContentState extends State { planStatus = PlanStatusValues.paid; } else if (widget.successfulPayment == false) { planStatus = PlanStatusValues.paymentFailed; - } else if (plan.chosenPlanState == ChosenPlanStateEnum.statePaid) { + } else if (plan.chosenPlanState.isStateAfterPaidState()) { planStatus = PlanStatusValues.paid; } else { planStatus = PlanStatusValues.pendingPayment; @@ -134,6 +135,12 @@ class _InfoByOrderScreenContentState extends State { 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( @@ -148,7 +155,10 @@ class _InfoByOrderScreenContentState extends State { backgroundColor: appTheme.whiteA700, ), const SizedBox(height: 8), - PlanProps(planDuration: plan.plan.duration, planPrice: chosenOption?.price.toDouble() ?? 0, planStatus: planStatus), + PlanProps( + planDuration: plan.plan.duration, + planPrice: chosenOption?.price ?? 0, + planStatus: planStatus), const SizedBox(height: 8), PlanInfo( title: plan.plan.name, diff --git a/lib/features/order_info/presentation/widgets/collect_charge_banner.dart b/lib/features/order_info/presentation/widgets/collect_charge_banner.dart new file mode 100644 index 00000000..cb1eaf1d --- /dev/null +++ b/lib/features/order_info/presentation/widgets/collect_charge_banner.dart @@ -0,0 +1,72 @@ +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_outlined_button.dart'; +import 'package:eatvio_app/core/widgets/main_background_bouble.dart'; +import 'package:eatvio_app/features/my_finance/presentation/pages/card_settings.dart'; +import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart'; +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +class CollectChargeBanner extends StatelessWidget { + const CollectChargeBanner({super.key, required this.chosenPlan, required this.price}); + final ChosenPlanModel chosenPlan; + final double price; + @override + Widget build(BuildContext context) { + final s = S.of(context); + return MainBackgroundBouble( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Stack( + children: [ + Positioned( + left: 0, + top: 0, + bottom: 0, + child: Container( + width: 4, + decoration: BoxDecoration( + color: appTheme.blueA400, borderRadius: const BorderRadius.only(topRight: Radius.circular(20), bottomRight: Radius.circular(20))), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only(right: 80), + child: Text( + '${s.congratsYouHaveFinishedPlan} «${chosenPlan.plan.name}»', + style: TextStyles.textMSemibold.copyWith( + color: appTheme.black, + ), + ), + ), + const SizedBox(height: 8), + Padding( + padding: const EdgeInsets.only(right: 80), + child: Text( + '${s.userConfirmedPlanCompletion} $price₽', + style: TextStyles.textSMedium.copyWith( + color: appTheme.black, + ), + ), + ), + const SizedBox(height: 12), + CustomOutlinedButton( + text: s.collectCharge, + height: 52, + buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), + buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.green)), + onPressed: () async { + GoRouter.of(context).push(CardSettingsScreen.path, extra: chosenPlan.id); + }, + ) + ], + ), + ) + ], + ), + ); + } +} diff --git a/lib/features/order_info/presentation/widgets/payment_banner.dart b/lib/features/order_info/presentation/widgets/payment_banner.dart index 316a318d..bd570581 100644 --- a/lib/features/order_info/presentation/widgets/payment_banner.dart +++ b/lib/features/order_info/presentation/widgets/payment_banner.dart @@ -14,6 +14,7 @@ class PaymentBanner extends StatefulWidget { const PaymentBanner({ super.key, required this.successfulPayment, + }); final bool successfulPayment; diff --git a/lib/features/plans/presentation/pages/about_plan_screen.dart b/lib/features/plans/presentation/pages/about_plan_screen.dart index 2935310f..f905901a 100644 --- a/lib/features/plans/presentation/pages/about_plan_screen.dart +++ b/lib/features/plans/presentation/pages/about_plan_screen.dart @@ -237,6 +237,7 @@ class _AboutPlanScreenState extends State { 'chatScreen', extra: { 'chatId': chosenPlan.chatId, + 'sendInitialMessage': true, }, ); }, diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 96602566..d27fa8ee 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -33,6 +33,9 @@ class MessageLookup extends MessageLookupByLibrary { ), "active_diet_state": MessageLookupByLibrary.simpleMessage("Активная"), "add": MessageLookupByLibrary.simpleMessage("Добавить"), + "addAtLeastOneItem": MessageLookupByLibrary.simpleMessage( + "Добавьте хотя бы одину опцию", + ), "add_card": MessageLookupByLibrary.simpleMessage("Привязать карту"), "add_cover": MessageLookupByLibrary.simpleMessage("Добавьте обложку"), "add_measure": MessageLookupByLibrary.simpleMessage("Добавить замер"), @@ -55,6 +58,9 @@ class MessageLookup extends MessageLookupByLibrary { "another_form_of_stage": MessageLookupByLibrary.simpleMessage("этапа"), "answer": MessageLookupByLibrary.simpleMessage("Ответить"), "archived": MessageLookupByLibrary.simpleMessage("В архиве"), + "atLeast3Symbols": MessageLookupByLibrary.simpleMessage( + "Минимум 3 символа", + ), "authentificate": MessageLookupByLibrary.simpleMessage("Войти"), "average_pressure": MessageLookupByLibrary.simpleMessage( "Средние показатели давления:", @@ -86,6 +92,10 @@ class MessageLookup extends MessageLookupByLibrary { "Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.", ), "carbohydrates": MessageLookupByLibrary.simpleMessage("Углеводы"), + "chargeApproved": MessageLookupByLibrary.simpleMessage("Выплата одобрена"), + "chargeCollectionFailed": MessageLookupByLibrary.simpleMessage( + "Не удалось оформить выплату", + ), "chat": MessageLookupByLibrary.simpleMessage("Чат"), "check_out_video_recipe": MessageLookupByLibrary.simpleMessage( "Посмотреть видео рецепт", @@ -105,6 +115,7 @@ class MessageLookup extends MessageLookupByLibrary { "choose_start_diet_date": MessageLookupByLibrary.simpleMessage( "Выберите дату старты диеты. Она отобразиться у Вас в дневнике", ), + "chosePlanType": MessageLookupByLibrary.simpleMessage("Выберите тип плана"), "chose_plan_start_date_2": MessageLookupByLibrary.simpleMessage( "Выберите дату старты плана. Она отобразиться в дневнике пользователя", ), @@ -119,6 +130,7 @@ class MessageLookup extends MessageLookupByLibrary { ), "city": MessageLookupByLibrary.simpleMessage("Город"), "city_of_living": MessageLookupByLibrary.simpleMessage("Город проживания"), + "collectCharge": MessageLookupByLibrary.simpleMessage("Получить выплату"), "comfort": MessageLookupByLibrary.simpleMessage("Комфорт"), "comment": MessageLookupByLibrary.simpleMessage("комментарий"), "commentForMeal": MessageLookupByLibrary.simpleMessage( @@ -138,9 +150,15 @@ class MessageLookup extends MessageLookupByLibrary { "Ваше обращение принято! Мы рассмотрим его в ближайшее время и примем необходимые меры. Спасибо за вашу бдительность и помощь в улучшении нашего сервиса.", ), "confirm": MessageLookupByLibrary.simpleMessage("Подтвердить"), + "congratsYouHaveFinishedPlan": MessageLookupByLibrary.simpleMessage( + "Поздравляем, Вы закончили план", + ), "consultations_count": MessageLookupByLibrary.simpleMessage( "Кол-во проведенных консулдьтаций", ), + "contactSupport": MessageLookupByLibrary.simpleMessage( + "Обратитесь в поддержку", + ), "continueButton": MessageLookupByLibrary.simpleMessage("Продолжить"), "continue_": MessageLookupByLibrary.simpleMessage("Продолжить"), "copied": MessageLookupByLibrary.simpleMessage("cкопирован"), @@ -382,6 +400,9 @@ class MessageLookup extends MessageLookupByLibrary { "Диета с мясом полезная и сбалансированная. Включает в себя нежирное мясо: курицу, индейку, кролика или говядину. В сочетании с овощами получаете все необходимые витамины и минералы. Не забывайте о порциях и старайтесь есть медленно, прислушиваясь к своему организму.", ), "milliliter": MessageLookupByLibrary.simpleMessage("мл"), + "minPlanPrice": MessageLookupByLibrary.simpleMessage( + "Минимальная сумма 1200₽", + ), "minimal_customer_price": MessageLookupByLibrary.simpleMessage( "Минимальная цена для покупателя: 1200₽", ), @@ -393,6 +414,9 @@ class MessageLookup extends MessageLookupByLibrary { "misrepresentation_description": MessageLookupByLibrary.simpleMessage( "Новости, которые могут вызвать широкий резонанс или несут угрозу, распространяемые с целью введения людей в заблуждение.", ), + "moneySentToCard": MessageLookupByLibrary.simpleMessage( + "Средства отправлены на карту", + ), "more_about_order": MessageLookupByLibrary.simpleMessage( "Подробнее о заказе", ), @@ -628,6 +652,9 @@ class MessageLookup extends MessageLookupByLibrary { "Верхние показатели давления:", ), "upper": MessageLookupByLibrary.simpleMessage("Верхнее"), + "userConfirmedPlanCompletion": MessageLookupByLibrary.simpleMessage( + "Пользователь подтвердил прохождения плана. В ближайшее время Вы получите", + ), "user_authorization": MessageLookupByLibrary.simpleMessage( "Аутентификация", ), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 260c15f5..441ca764 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -3064,6 +3064,116 @@ class S { args: [], ); } + + /// `Поздравляем, Вы закончили план` + String get congratsYouHaveFinishedPlan { + return Intl.message( + 'Поздравляем, Вы закончили план', + name: 'congratsYouHaveFinishedPlan', + desc: '', + args: [], + ); + } + + /// `Пользователь подтвердил прохождения плана. В ближайшее время Вы получите` + String get userConfirmedPlanCompletion { + return Intl.message( + 'Пользователь подтвердил прохождения плана. В ближайшее время Вы получите', + name: 'userConfirmedPlanCompletion', + desc: '', + args: [], + ); + } + + /// `Получить выплату` + String get collectCharge { + return Intl.message( + 'Получить выплату', + name: 'collectCharge', + desc: '', + args: [], + ); + } + + /// `Выплата одобрена` + String get chargeApproved { + return Intl.message( + 'Выплата одобрена', + name: 'chargeApproved', + desc: '', + args: [], + ); + } + + /// `Средства отправлены на карту` + String get moneySentToCard { + return Intl.message( + 'Средства отправлены на карту', + name: 'moneySentToCard', + desc: '', + args: [], + ); + } + + /// `Не удалось оформить выплату` + String get chargeCollectionFailed { + return Intl.message( + 'Не удалось оформить выплату', + name: 'chargeCollectionFailed', + desc: '', + args: [], + ); + } + + /// `Обратитесь в поддержку` + String get contactSupport { + return Intl.message( + 'Обратитесь в поддержку', + name: 'contactSupport', + desc: '', + args: [], + ); + } + + /// `Минимум 3 символа` + String get atLeast3Symbols { + return Intl.message( + 'Минимум 3 символа', + name: 'atLeast3Symbols', + desc: '', + args: [], + ); + } + + /// `Выберите тип плана` + String get chosePlanType { + return Intl.message( + 'Выберите тип плана', + name: 'chosePlanType', + desc: '', + args: [], + ); + } + + /// `Минимальная сумма 1200₽` + String get minPlanPrice { + return Intl.message( + 'Минимальная сумма 1200₽', + name: 'minPlanPrice', + desc: '', + args: [], + ); + } + + /// `Добавьте хотя бы одину опцию` + String get addAtLeastOneItem { + return Intl.message( + 'Добавьте хотя бы одину опцию', + name: 'addAtLeastOneItem', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index b0f45e86..c8e5a2c6 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -369,6 +369,17 @@ "orderCanceled": "Заказ отменен", "couldNotCancelOrder": "Не удалось отменить заказ", "dayComment": "Комментарий ко всему дню", - "mealComment": "Комментарий к приему пищи" + "mealComment": "Комментарий к приему пищи", + "congratsYouHaveFinishedPlan": "Поздравляем, Вы закончили план", + "userConfirmedPlanCompletion": "Пользователь подтвердил прохождения плана. В ближайшее время Вы получите", + "collectCharge": "Получить выплату", + "chargeApproved": "Выплата одобрена", + "moneySentToCard": "Средства отправлены на карту", + "chargeCollectionFailed": "Не удалось оформить выплату", + "contactSupport": "Обратитесь в поддержку", + "atLeast3Symbols": "Минимум 3 символа", + "chosePlanType": "Выберите тип плана", + "minPlanPrice": "Минимальная сумма 1200₽", + "addAtLeastOneItem": "Добавьте хотя бы одину опцию" } From b74d683e2c2dd1656d66f286e93eae56fbefb3af Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Tue, 29 Apr 2025 11:42:20 +0300 Subject: [PATCH 11/26] feat: add welcome screen --- assets/images/logo_welcome.svg | 9 ++ assets/splash/bg.json | 1 + assets/splash/bg.lottie | Bin 0 -> 1374 bytes lib/core/app_router.dart | 18 ++- .../presentation/pages/welcome_screen.dart | 107 ++++++++++++++++++ pubspec.yaml | 1 + 6 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 assets/images/logo_welcome.svg create mode 100644 assets/splash/bg.json create mode 100644 assets/splash/bg.lottie create mode 100644 lib/features/welcome/presentation/pages/welcome_screen.dart diff --git a/assets/images/logo_welcome.svg b/assets/images/logo_welcome.svg new file mode 100644 index 00000000..37102693 --- /dev/null +++ b/assets/images/logo_welcome.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/splash/bg.json b/assets/splash/bg.json new file mode 100644 index 00000000..4e4acd76 --- /dev/null +++ b/assets/splash/bg.json @@ -0,0 +1 @@ +{"nm":"background","ddd":0,"h":1920,"w":1080,"meta":{"g":"@lottiefiles/toolkit-js 0.33.2"},"layers":[{"ty":4,"nm":"Shape Layer 1","sr":1,"st":0,"op":155,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[540,960,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Rectangle 1","ix":1,"cix":2,"np":3,"it":[{"ty":"rc","bm":0,"hd":false,"mn":"ADBE Vector Shape - Rect","nm":"Rectangle Path 1","d":1,"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"s":{"a":0,"k":[1160,880]}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100},"w":{"a":0,"k":6},"c":{"a":0,"k":[1,1,1]}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[374.412,342.611],"t":0},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[22.822,357.191],"t":31},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[-420.714,389.994],"t":61},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[50.932,404.573],"t":91},{"s":[376.797,364.48],"t":120}]},"g":{"p":3,"k":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,0.00784313725490196,0.4392156862745098,1,0.5,0.10196078431372549,0.4666666666666667,0.9411764705882353,1,0.9490196078431372,0.9490196078431372,0.9490196078431372],"t":0},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,0.3411764705882353,0.6039215686274509,0.9411764705882353,0.5,0.0392156862745098,0.4549019607843137,1,1,0.1568627450980392,0.42745098039215684,0.8823529411764706],"t":31},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,0.3176470588235294,0.5568627450980392,0.8666666666666667,0.5,0.6392156862745098,0.796078431372549,1,1,1,1,1],"t":61},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,0.1843137254901961,0.5019607843137255,0.9294117647058824,0.5,0.0784313725490196,0.596078431372549,0.7529411764705882,1,0,0.43529411764705883,1],"t":91},{"s":[0,0.00784313725490196,0.4392156862745098,1,0.5,0.5686274509803921,0.7411764705882353,0.9686274509803922,1,0.29411764705882354,0.5529411764705883,0.8862745098039215],"t":120}]}},"t":1,"a":{"a":0,"k":0},"h":{"a":0,"k":0},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[-430.769,-404.573],"t":0},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[23.726,-364.48],"t":31},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[312.726,-353.48],"t":61},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[29.739,-353.48],"t":91},{"s":[-407.606,-357.125],"t":120}]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[93.29,219.491],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1},{"ty":1,"nm":"Deep Red Solid 1","sr":1,"st":0,"op":155,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[540,960,0]},"s":{"a":0,"k":[100,100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[540,960,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"ef":[],"sc":"#5095ef","sh":1920,"sw":1080,"ind":2}],"v":"4.6.10","fr":15,"op":155,"ip":0,"assets":[]} \ No newline at end of file diff --git a/assets/splash/bg.lottie b/assets/splash/bg.lottie new file mode 100644 index 0000000000000000000000000000000000000000..b6974b15964fb0f6efe43c733c2b13b9434186b7 GIT binary patch literal 1374 zcmWIWW@Zs#U|`^2$OxSi^;<^&eFKoU6^MC(I5#mbGcC2aL@%p2Kd-mfk?)X#fa`ms z?3GKGM(ldUYSUQgczA&X7n_^#qlf8-DeCD?Z0}(_`lvvqN=v_XnUjwd2;O!gb6H3|>HYngE>&bZKs4NoIatvA%_g zp`}@>nSrjMsYR-;Nvd(0uBB;Ol5SF(S)x%|nwhb=i6z)aXTxvzJ#rKHD|)3e*Qz$( z+~0G{$$ct8Qzm}eG>ctd;#fDkK$)KQk^Rp@Za3&}zLX>3DtY8WNND(6tI)f8emURI zh`mYPcPFp!w|$xRv71?2lekab6pmi8V&e~P?wN@nV@xcg=iFe6-naUjZ?M?wOt4A@0mizozfG7kBliUFY_WiX=|8e-jxvRF1zEoS}Eh zZ{3n7cdauOU$0bkakzN@!I~!*%g-^?q{Q++p2V@DN{GeEIbzMj*Mi@U9PL%wX4y2s zyKeiL`Q;aC|G#UOPv0+oI<0=rUiYKY)+Lsq`Ht?S< z+^?%Aa66wfYOBwURsJs@@AUGu(DiurVcr?%rn$^>WY)`_IQuKxao_y^Az@cpRi3Qg zbi>qGNNtU)xPXJ^gOx`+mAn?#Jm3ku%yr|`YBl2(xt9{IZu)(YMSIPqI}y5jf?gO| zMYjC8Uv(uaMT#$0K&yKzIk|hE+vog?Yc)^XRsB%^6YO$xOTv_;^V1~V zj{1rF>`J-!KtmwGG-vOXw8LV`DIeWLmrlF1Xp@ihb>CgaUn-WJ4Ow>Op|fRFNsYw* zUj;og)Cy;qg~&vyIwW;m+_9}lx#fy^GsC)i=Z4Uzw9>eJf0s~2;yWq!B4SH+&g5@VpLG|#;%V5Lc6 z!_Vn$>)xHbd!fTrmj9uuuis@s)ucJSF{?Fls(wt2Y_gIPi|(C!uE+d+m6gy|u2~kx zHYcwx)bE|JR%^~g!^&e<{0`h^nOHl^;GXDF#>Ka@zHL~4>T11NCO_Mg|F^gFF6^sq zHh!nD@Zs-I%**%vJLDOYmGssBM%2TPF>Nnr&0r2qKKE&tp46*T$3NQ#cr!AIFyJn% nfQ0}OKq)d3U literal 0 HcmV?d00001 diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index 58ccde6e..0e8cd579 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -53,15 +53,17 @@ import 'package:eatvio_app/features/settings/presentation/pages/pressure_screen. import 'package:eatvio_app/features/settings/presentation/pages/settings_screen.dart'; import 'package:eatvio_app/features/support/presentation/pages/support_screen.dart'; import 'package:eatvio_app/features/user_details/presentation/user_details_screen.dart'; +import 'package:eatvio_app/features/welcome/presentation/pages/welcome_screen.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; +import 'package:shared_preferences/shared_preferences.dart'; class AppRouter { static GlobalKey navigatorKey = GlobalKey(); static GoRouter appRouter = GoRouter( - initialLocation: '/my-profile-screen', + initialLocation: '/welcome-screen', navigatorKey: navigatorKey, routes: [ GoRoute( @@ -123,6 +125,20 @@ class AppRouter { ), ], ), + GoRoute( + path: '/welcome-screen', + name: 'welcomeScreen', + builder: (context, state) => const WelcomeScreen(), + redirect: (context, state) async { + final sharedPreferences = await SharedPreferences.getInstance(); + final welcomeScreenWasShowed = sharedPreferences.getBool('welcomeScreenWasShowed'); + if (welcomeScreenWasShowed == true) { + return '/my-profile-screen'; + } else { + return null; + } + }, + ), GoRoute( path: '/consultant-finance-screen', name: 'consultantFinanceScreen', diff --git a/lib/features/welcome/presentation/pages/welcome_screen.dart b/lib/features/welcome/presentation/pages/welcome_screen.dart new file mode 100644 index 00000000..d04528db --- /dev/null +++ b/lib/features/welcome/presentation/pages/welcome_screen.dart @@ -0,0 +1,107 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:go_router/go_router.dart'; +import 'package:lottie/lottie.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../../../core/theme/theme_helper.dart'; + +class WelcomeScreen extends StatelessWidget { + const WelcomeScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack( + clipBehavior: Clip.none, + children: [ + Positioned( + left: -20, + top: -20, + right: -20, + bottom: -20, + child: Lottie.asset( + 'assets/splash/bg.json', + fit: BoxFit.cover, + ), + ), + Positioned.fill( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 40), + SvgPicture.asset('assets/images/logo_welcome.svg'), + const Spacer(), + Padding( + padding: const EdgeInsets.all(16), + child: Material( + borderRadius: BorderRadius.circular(14), + color: Colors.white, + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + const SizedBox(height: 20), + const Text( + 'Добро пожаловать\nв EATVIO! 🚀', + style: TextStyle( + fontSize: 28, + fontWeight: FontWeight.w800, + height: 1, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 16), + const Text( + 'Это первая версия приложения, и мы активно работаем над его улучшением. Если вы заметите ошибку или у вас есть идея — пожалуйста, сообщите нам! Ваша помощь бесценна ❤️', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + height: 1, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 32), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 52), + child: InkWell( + onTap: () async { + final sharedPreferences = await SharedPreferences.getInstance(); + await sharedPreferences.setBool('welcomeScreenWasShowed', true); + GoRouter.of(context).go('/my-profile-screen'); + }, + borderRadius: BorderRadius.circular(20), + child: Ink( + height: 52, + decoration: BoxDecoration( + color: appTheme.blueA400, + borderRadius: BorderRadius.circular(20), + ), + child: const Center( + child: Text( + 'Начать пользоваться', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + height: 1, + color: Colors.white, + ), + ), + ), + ), + ), + ), + const SizedBox(height: 20), + ], + ), + ), + ), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 28097a40..bce4371f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -67,6 +67,7 @@ dependencies: http_parser: ^4.1.2 path: ^1.9.0 collection: ^1.19.0 + shared_preferences: ^2.5.3 dependency_overrides: intl: ^0.19.0 From 6b48eaf8fea9ce6eb5f0864cfddb2cff649e1b53 Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Tue, 29 Apr 2025 12:01:13 +0300 Subject: [PATCH 12/26] feat: update screen --- lib/core/app_router.dart | 24 +++- .../presentation/update_screen.dart | 109 ++++++++++++++++++ .../presentation/pages/welcome_screen.dart | 17 ++- pubspec.yaml | 2 + 4 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 lib/features/update_screen/presentation/update_screen.dart diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index 0e8cd579..930ab4d0 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -1,3 +1,5 @@ +import 'package:dio/dio.dart'; +import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/websockets/ws_manager.dart'; import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/features/auth/presentation/pages/auth_screen.dart'; @@ -52,18 +54,21 @@ import 'package:eatvio_app/features/settings/presentation/pages/physical_form_sc import 'package:eatvio_app/features/settings/presentation/pages/pressure_screen.dart'; import 'package:eatvio_app/features/settings/presentation/pages/settings_screen.dart'; import 'package:eatvio_app/features/support/presentation/pages/support_screen.dart'; +import 'package:eatvio_app/features/update_screen/presentation/update_screen.dart'; import 'package:eatvio_app/features/user_details/presentation/user_details_screen.dart'; import 'package:eatvio_app/features/welcome/presentation/pages/welcome_screen.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; +import 'package:package_info_plus/package_info_plus.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:version/version.dart'; class AppRouter { static GlobalKey navigatorKey = GlobalKey(); static GoRouter appRouter = GoRouter( - initialLocation: '/welcome-screen', + initialLocation: '/update-screen', navigatorKey: navigatorKey, routes: [ GoRoute( @@ -139,6 +144,23 @@ class AppRouter { } }, ), + GoRoute( + path: '/update-screen', + name: 'updateScreen', + builder: (context, state) => const UpdateScreen(), + redirect: (context, state) async { + final versions = (await sl().get('$baseUrl/api/app_versions')).data; + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + String version = packageInfo.version; + Version currentVersion = Version.parse(version); + Version latestVersion = Version.parse(versions['min_version']); + if (latestVersion > currentVersion) { + return null; + } else { + return '/welcome-screen'; + } + }, + ), GoRoute( path: '/consultant-finance-screen', name: 'consultantFinanceScreen', diff --git a/lib/features/update_screen/presentation/update_screen.dart b/lib/features/update_screen/presentation/update_screen.dart new file mode 100644 index 00000000..5a897096 --- /dev/null +++ b/lib/features/update_screen/presentation/update_screen.dart @@ -0,0 +1,109 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:lottie/lottie.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import '../../../../core/theme/theme_helper.dart'; + +class UpdateScreen extends StatelessWidget { + const UpdateScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack( + clipBehavior: Clip.none, + children: [ + Positioned( + left: -20, + top: -20, + right: -20, + bottom: -20, + child: Lottie.asset( + 'assets/splash/bg.json', + fit: BoxFit.cover, + ), + ), + Positioned.fill( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 40), + SvgPicture.asset('assets/images/logo_welcome.svg'), + const Spacer(), + Padding( + padding: const EdgeInsets.all(16), + child: Material( + borderRadius: BorderRadius.circular(14), + color: Colors.white, + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + const SizedBox(height: 20), + const Text( + 'Пора обновиться! ⚡', + style: TextStyle( + fontSize: 28, + fontWeight: FontWeight.w800, + height: 1, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 16), + const Text( + 'Мы выпустили важное обновление с новыми функциями и исправлениями. Чтобы продолжить пользоваться приложением без сбоев, обновите его прямо сейчас.', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + height: 1, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 32), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 52), + child: InkWell( + onTap: () async { + final Uri url = Platform.isAndroid + ? Uri.parse('https://www.rustore.ru/catalog/app/com.eatvioapp.app') + : Uri.parse('https://apps.apple.com/app/idYOUR_APP_ID'); + await launchUrl(url); + }, + borderRadius: BorderRadius.circular(20), + child: Ink( + height: 52, + decoration: BoxDecoration( + color: appTheme.blueA400, + borderRadius: BorderRadius.circular(20), + ), + child: const Center( + child: Text( + 'Обновить', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + height: 1, + color: Colors.white, + ), + ), + ), + ), + ), + ), + const SizedBox(height: 20), + ], + ), + ), + ), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/features/welcome/presentation/pages/welcome_screen.dart b/lib/features/welcome/presentation/pages/welcome_screen.dart index d04528db..6ede4081 100644 --- a/lib/features/welcome/presentation/pages/welcome_screen.dart +++ b/lib/features/welcome/presentation/pages/welcome_screen.dart @@ -6,9 +6,22 @@ import 'package:shared_preferences/shared_preferences.dart'; import '../../../../core/theme/theme_helper.dart'; -class WelcomeScreen extends StatelessWidget { +class WelcomeScreen extends StatefulWidget { const WelcomeScreen({super.key}); + @override + State createState() => _WelcomeScreenState(); +} + +class _WelcomeScreenState extends State { + late final GoRouter goRouter; + + @override + void initState() { + super.initState(); + goRouter = GoRouter.of(context); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -68,7 +81,7 @@ class WelcomeScreen extends StatelessWidget { onTap: () async { final sharedPreferences = await SharedPreferences.getInstance(); await sharedPreferences.setBool('welcomeScreenWasShowed', true); - GoRouter.of(context).go('/my-profile-screen'); + goRouter.go('/my-profile-screen'); }, borderRadius: BorderRadius.circular(20), child: Ink( diff --git a/pubspec.yaml b/pubspec.yaml index bce4371f..dfb0838a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -68,6 +68,8 @@ dependencies: path: ^1.9.0 collection: ^1.19.0 shared_preferences: ^2.5.3 + version: ^3.0.2 + package_info_plus: ^8.3.0 dependency_overrides: intl: ^0.19.0 From 66196a33326746382f14ee43c05fe0dd2d664942 Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Tue, 29 Apr 2025 13:06:09 +0300 Subject: [PATCH 13/26] feat: custom popup for blogPosts --- assets/icons/edit_paint.svg | 11 ++ assets/icons/red_trash.svg | 6 + assets/icons/remove_from_public.svg | 5 + lib/core/widgets/custom_popup.dart | 175 ++++++++++++++++++ .../presentation/pages/blog_post_screen.dart | 44 +---- .../widgets/blog_post_widget.dart | 33 +--- 6 files changed, 212 insertions(+), 62 deletions(-) create mode 100644 assets/icons/edit_paint.svg create mode 100644 assets/icons/red_trash.svg create mode 100644 assets/icons/remove_from_public.svg create mode 100644 lib/core/widgets/custom_popup.dart diff --git a/assets/icons/edit_paint.svg b/assets/icons/edit_paint.svg new file mode 100644 index 00000000..e4a9eeef --- /dev/null +++ b/assets/icons/edit_paint.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/icons/red_trash.svg b/assets/icons/red_trash.svg new file mode 100644 index 00000000..3864fb63 --- /dev/null +++ b/assets/icons/red_trash.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/remove_from_public.svg b/assets/icons/remove_from_public.svg new file mode 100644 index 00000000..36213c53 --- /dev/null +++ b/assets/icons/remove_from_public.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/lib/core/widgets/custom_popup.dart b/lib/core/widgets/custom_popup.dart new file mode 100644 index 00000000..967f6fee --- /dev/null +++ b/lib/core/widgets/custom_popup.dart @@ -0,0 +1,175 @@ +import 'package:eatvio_app/core/const.dart'; +import 'package:eatvio_app/core/enums/complained_content.dart'; +import 'package:eatvio_app/core/locator_service.dart'; +import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/core/utils/toasts.dart'; +import 'package:eatvio_app/core/widgets/show_error_dialog.dart'; +import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_cubit.dart'; +import 'package:eatvio_app/features/complaining/presentation/complaining_bottom_sheet.dart'; +import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart'; +import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart'; +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:go_router/go_router.dart'; +import 'package:share_plus/share_plus.dart'; + +class CustomPopup extends StatelessWidget { + final bool isAuthor; + final ComplainedContent? complainedContentType; + final String path; + final String? slug; + final int? id; + final FeedBlogPostModel? feedBlogPostModel; + + const CustomPopup({ + super.key, + required this.isAuthor, + this.complainedContentType, + required this.path, + this.id, + this.slug, + this.feedBlogPostModel, + }); + + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return GestureDetector( + onTap: () async { + final RenderBox button = context.findRenderObject() as RenderBox; + final RenderBox overlay = Overlay.of(context).context.findRenderObject() as RenderBox; + final Offset position = button.localToGlobal(Offset.zero, ancestor: overlay); + + await showMenu( + context: context, + position: RelativeRect.fromLTRB( + position.dx, + position.dy + button.size.height, + position.dx + button.size.width, + position.dy, + ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + color: Colors.white, + constraints: const BoxConstraints(minWidth: 260), + surfaceTintColor: Colors.transparent, + elevation: 2, + items: _buildMenuItems(s, context), + ); + }, + child: const Icon( + Icons.more_horiz_rounded, + ), + ); + } + + List _buildMenuItems(s, context) { + final entries = []; + + void addItem(String icon, String text, Function() onTap) { + entries.add(_buildCompactItem(icon: icon, text: text, onTap: onTap)); + entries.add(_buildDivider()); + } + + if (isAuthor) { + addItem('assets/icons/copy2.svg', 'Скопировать ссылку', () { + Clipboard.setData(ClipboardData(text: '$baseUrl/ru/$path')); + showInfoToast(s.link_was_copied); + }); + addItem('assets/icons/share2.svg', 'Поделиться', () { + final box = context.findRenderObject() as RenderBox?; + Share.shareUri( + Uri.parse( + '$baseUrl/ru/$path', + ), + sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size, + ); + }); + addItem('assets/icons/edit_paint.svg', 'Редактировать', () { + if (complainedContentType == ComplainedContent.posts) { + GoRouter.of(context).pushNamed('blogPostCreateScreen', extra: feedBlogPostModel); + } + }); + if (complainedContentType == ComplainedContent.plan || complainedContentType == ComplainedContent.diet) { + addItem('assets/icons/remove_from_public.svg', 'Снять с публикации', () {}); + } + addItem('assets/icons/red_trash.svg', 'Удалить', () async { + if (complainedContentType == ComplainedContent.posts) { + final result = await sl().deleteBlogPost(id!); + if (!context.mounted) return; + if (!result) { + showErrorDialog(context, title: S.current.could_not_delete_blog_post); + return; + } + context.read().loadFeedBlogPostList(); + } + }); + } else { + addItem('assets/icons/copy2.svg', 'Скопировать ссылку', () { + Clipboard.setData(ClipboardData(text: '$baseUrl/ru/$path')); + showInfoToast(s.link_was_copied); + }); + addItem('assets/icons/share2.svg', 'Поделиться', () { + final box = context.findRenderObject() as RenderBox?; + Share.shareUri( + Uri.parse( + '$baseUrl/ru/$path', + ), + sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size, + ); + }); + addItem('assets/icons/complain.svg', 'Пожаловаться', () { + if (complainedContentType != null) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) => ComplainingBottomSheet( + complainedContentType: complainedContentType!, + id: id, + slug: slug, + ), + ); + } + }); + } + + if (entries.isNotEmpty && entries.last is PopupMenuItem) { + entries.removeLast(); + } + + return entries; + } + + PopupMenuItem _buildCompactItem({required String icon, required String text, required Function() onTap}) { + return PopupMenuItem( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), + height: 36, + onTap: onTap, + child: SizedBox( + width: double.infinity, + child: Row( + children: [ + Text(text, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500)), + const Spacer(), + SvgPicture.asset(icon), + ], + ), + ), + ); + } + + PopupMenuItem _buildDivider() { + return PopupMenuItem( + enabled: false, + height: 1, + padding: EdgeInsets.zero, + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 12), + height: 1, + color: appTheme.dividerColor, + ), + ); + } +} diff --git a/lib/features/blog_post/presentation/pages/blog_post_screen.dart b/lib/features/blog_post/presentation/pages/blog_post_screen.dart index 63f87233..e963a812 100644 --- a/lib/features/blog_post/presentation/pages/blog_post_screen.dart +++ b/lib/features/blog_post/presentation/pages/blog_post_screen.dart @@ -3,10 +3,8 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/enums/complained_content.dart'; import 'package:eatvio_app/core/utils/date_time_utils.dart'; -import 'package:eatvio_app/core/widgets/show_error_dialog.dart'; -import 'package:eatvio_app/features/complaining/presentation/complaining_bottom_sheet.dart'; +import 'package:eatvio_app/core/widgets/custom_popup.dart'; import 'package:eatvio_app/core/widgets/loader.dart'; -import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_cubit.dart'; import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_state.dart'; @@ -14,7 +12,6 @@ import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart'; import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; -import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_html/flutter_html.dart'; @@ -163,39 +160,14 @@ class _BlogPostScreenState extends State { appBar: AppBar( title: Text(widget.blogPost.title), actions: [ - PopupMenuButton( - onSelected: (value) async { - if (value == 'delete') { - final result = await sl().deleteBlogPost(widget.blogPost.id); - if (!context.mounted) return; - if (!result) { - showErrorDialog(context, title: S.current.could_not_delete_blog_post); - return; - } - context.read().loadFeedBlogPostList(); - Navigator.of(context).pop(); - } - if (value == 'edit') { - GoRouter.of(context).replaceNamed('blogPostCreateScreen', extra: widget.blogPost); - } - }, - itemBuilder: (context) => [ - if (widget.blogPost.author.authorIsMe) const PopupMenuItem(value: 'edit', child: Text('Редактировать')), - if (widget.blogPost.author.authorIsMe) const PopupMenuItem(value: 'delete', child: Text('Удалить')), - if (!widget.blogPost.author.authorIsMe) - PopupMenuItem( - value: 'report', - child: const Text('Пожаловаться'), - onTap: () { - showModalBottomSheet( - context: context, - isScrollControlled: true, - builder: (context) => ComplainingBottomSheet(complainedContentType: ComplainedContent.posts, id: widget.blogPost.id), - ); - }, - ), - ], + CustomPopup( + isAuthor: widget.blogPost.author.authorIsMe, + complainedContentType: ComplainedContent.posts, + path: 'blog_post/${(widget.blogPost.id)}', + id: widget.blogPost.id, + feedBlogPostModel: widget.blogPost, ), + const SizedBox(width: 16), ], ), body: SingleChildScrollView( diff --git a/lib/features/feed/presentation/widgets/blog_post_widget.dart b/lib/features/feed/presentation/widgets/blog_post_widget.dart index 605df679..f5b8dd2d 100644 --- a/lib/features/feed/presentation/widgets/blog_post_widget.dart +++ b/lib/features/feed/presentation/widgets/blog_post_widget.dart @@ -4,9 +4,9 @@ import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/enums/complained_content.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_popup.dart'; import 'package:eatvio_app/core/widgets/feed_icon_button.dart'; import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.dart'; -import 'package:eatvio_app/features/diets/presentation/widgets/more_options.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_state.dart'; @@ -14,7 +14,6 @@ import 'package:eatvio_app/features/feed/presentation/widgets/subscribe_button_w import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_html/flutter_html.dart'; -import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:go_router/go_router.dart'; import 'package:share_plus/share_plus.dart'; @@ -119,30 +118,12 @@ class _BlogPostWidgetState extends State { slug: state.feedBlogPostList[widget.index].author.authorSlug, ), ), - PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - complainedContentType: ComplainedContent.posts, - path: 'blog_post/${(state.feedBlogPostList[widget.index].id)}', - id: state.feedBlogPostList[widget.index].id, - ), - anchor: const Aligned(shiftToWithinBound: AxisFlag(x: true, y: true), follower: Alignment.topRight, target: Alignment.bottomRight), - child: InkWell( - onTap: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - child: SizedBox( - width: 32, - height: 24, - child: Material( - color: Colors.white, - borderRadius: BorderRadius.circular(4), - child: const Icon(Icons.more_horiz), - ), - ), - ), + CustomPopup( + isAuthor: state.feedBlogPostList[widget.index].author.authorIsMe, + complainedContentType: ComplainedContent.posts, + path: 'blog_post/${(state.feedBlogPostList[widget.index].id)}', + id: state.feedBlogPostList[widget.index].id, + feedBlogPostModel: state.feedBlogPostList[widget.index], ), ], ), From d040f9497924e3fd7d74c52a503df9007c997442 Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Wed, 30 Apr 2025 10:57:30 +0300 Subject: [PATCH 14/26] feat(recipes): custom popup for recipes --- lib/features/feed/presentation/widgets/recipes_widget.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/features/feed/presentation/widgets/recipes_widget.dart b/lib/features/feed/presentation/widgets/recipes_widget.dart index 2a586190..877aa19b 100644 --- a/lib/features/feed/presentation/widgets/recipes_widget.dart +++ b/lib/features/feed/presentation/widgets/recipes_widget.dart @@ -113,6 +113,7 @@ class _RecipesWidgetState extends State { size: 19, ), ), + // Testing Issue 329 Positioned( right: 12, top: 12, From 37ed041da4e6aa661b6e511c72e6317b08b03358 Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Wed, 30 Apr 2025 12:18:07 +0300 Subject: [PATCH 15/26] fix: test-flight --- .gitea/workflows/test-flight-cd.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitea/workflows/test-flight-cd.yaml b/.gitea/workflows/test-flight-cd.yaml index 2a4f5aec..1bb78c94 100644 --- a/.gitea/workflows/test-flight-cd.yaml +++ b/.gitea/workflows/test-flight-cd.yaml @@ -11,6 +11,9 @@ jobs: - name: Checkout uses: actions/checkout@v2 + - name: Remove cache + run: rm -rf /Users/gitea_runner_ci/Library/Developer/Xcode/DerivedData/* + - name: Download provisioning uses: mso7nueh/apple-provisioning-profiles-downloader@v1.0.8 with: From 32d698bf8206431dc76c8e555ba53ad7f92e8f0f Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Wed, 30 Apr 2025 16:02:39 +0300 Subject: [PATCH 16/26] feat(support): add docs --- .../presentation/pages/support_screen.dart | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/lib/features/support/presentation/pages/support_screen.dart b/lib/features/support/presentation/pages/support_screen.dart index 5a80e62b..d4bef652 100644 --- a/lib/features/support/presentation/pages/support_screen.dart +++ b/lib/features/support/presentation/pages/support_screen.dart @@ -1,4 +1,6 @@ +import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/utils/toasts.dart'; +import 'package:eatvio_app/core/widgets/custom_divider.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -17,6 +19,7 @@ class SupportScreen extends StatelessWidget { title: const Text('Поддержка'), ), body: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ const Padding( padding: EdgeInsets.all(16), @@ -85,6 +88,60 @@ class SupportScreen extends StatelessWidget { ), ), ), + const CustomDivider(), + const SizedBox(height: 16), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), + child: Text( + 'Правовые документы', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w500, + ), + ), + ), + const SizedBox(height: 12), + InkWell( + onTap: () async => await launchUrl(Uri.parse('$baseUrl/public_offer.pdf')), + borderRadius: BorderRadius.circular(16), + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4), + child: Text( + 'Публичная офёрта', + style: TextStyle( + fontSize: 16, + ), + ), + ), + ), + const SizedBox(height: 8), + InkWell( + onTap: () async => await launchUrl(Uri.parse('$baseUrl/docs/privacy.pdf')), + borderRadius: BorderRadius.circular(16), + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4), + child: Text( + 'Политика конфиденциальности', + style: TextStyle( + fontSize: 16, + ), + ), + ), + ), + const SizedBox(height: 8), + InkWell( + onTap: () async => await launchUrl(Uri.parse('$baseUrl/company-details')), + borderRadius: BorderRadius.circular(16), + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4), + child: Text( + 'О компании', + style: TextStyle( + fontSize: 16, + ), + ), + ), + ), ], ), ); From 3b4ed501fb0b7cc62d2c00d4ffe1190844dad462 Mon Sep 17 00:00:00 2001 From: Username1548 Date: Wed, 30 Apr 2025 21:25:23 +0700 Subject: [PATCH 17/26] feat(chats,diary): added logic for chosen diet portions and added banner to finish plan to chats --- ios/Runner.xcodeproj/project.pbxproj | 33 ++-- lib/core/app_router.dart | 2 +- lib/core/const.dart | 4 +- lib/core/locator_service.dart | 4 +- lib/core/widgets/bottom_sheet_with_blur.dart | 2 +- .../chat/models/plan_complain_model.dart | 14 ++ .../bloc/chat_room_cubit/chat_room_cubit.dart | 27 ++- .../chat/presentation/pages/chat_screen.dart | 8 +- .../widgets/complete_plan_bottom_sheet.dart | 102 ++++++++++++ .../widgets/notification_banner.dart | 72 +++++++- .../widgets/plan_complain_bottom_sheet.dart | 154 ++++++++++++++++++ .../chat/repositories/chat_repository.dart | 22 +++ lib/features/chat/services/chat_service.dart | 12 ++ .../models/consultation_model.dart | 5 +- .../services/consultation_service.dart | 3 +- .../diary/models/diary_day_model.dart | 5 +- .../diary/presentation/bloc/diary_cubit.dart | 16 +- .../presentation/pages/diary_screen.dart | 7 +- .../presentation/widgets/product_widget.dart | 49 +++--- .../feed/models/plan_pagination_model.dart | 13 +- .../internet_connection_checker_screen.dart | 2 +- .../presentation/info_by_order_screen.dart | 2 +- .../plans/enums/chosen_plan_state_enum.dart | 1 + .../presentation/pages/welcome_screen.dart | 4 +- lib/generated/intl/messages_en.dart | 12 ++ lib/generated/l10n.dart | 40 +++++ lib/l10n/intl_en.arb | 6 +- pubspec.yaml | 2 +- 28 files changed, 552 insertions(+), 71 deletions(-) create mode 100644 lib/features/chat/models/plan_complain_model.dart create mode 100644 lib/features/chat/presentation/widgets/complete_plan_bottom_sheet.dart create mode 100644 lib/features/chat/presentation/widgets/plan_complain_bottom_sheet.dart diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index c67b16dc..c2ccb23a 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -386,12 +386,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CODE_SIGN_IDENTITY = "Apple Distribution"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 7; - DEVELOPMENT_TEAM = "VL68C36HYS"; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = VL68C36HYS; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -411,8 +409,7 @@ MARKETING_VERSION = 1.0.50; PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = "Distribution"; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Distribution; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; @@ -539,12 +536,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CODE_SIGN_IDENTITY = "Apple Distribution"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 7; - DEVELOPMENT_TEAM = "VL68C36HYS"; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = VL68C36HYS; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -564,8 +559,7 @@ MARKETING_VERSION = 1.0.50; PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = "Distribution"; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Distribution; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -580,12 +574,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CODE_SIGN_IDENTITY = "Apple Distribution"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 7; - DEVELOPMENT_TEAM = "VL68C36HYS"; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = VL68C36HYS; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -605,8 +597,7 @@ MARKETING_VERSION = 1.0.50; PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = "Distribution"; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Distribution; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index e596c98e..63ec1094 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -223,7 +223,7 @@ class AppRouter { builder: (context, state) { final data = state.extra as Map; final chatId = data['chatId'] as String; - final sendInitialMessage = data['sendInitialMessage'] as bool; + final sendInitialMessage = data['sendInitialMessage'] as bool?; return ChatScreen(chatId: chatId, sendInitialMessage: sendInitialMessage,); }, ), diff --git a/lib/core/const.dart b/lib/core/const.dart index 31c47e9c..4f38a54e 100644 --- a/lib/core/const.dart +++ b/lib/core/const.dart @@ -1,5 +1,5 @@ -String baseUrl = 'https://eatvio.ru'; -// String baseUrl = 'https://dev2.eatvio.com'; +// String baseUrl = 'https://eatvio.ru'; +String baseUrl = 'https://dev2.eatvio.com'; const List durationValues = [7, 14, 21]; diff --git a/lib/core/locator_service.dart b/lib/core/locator_service.dart index ebd8d539..c2c27015 100644 --- a/lib/core/locator_service.dart +++ b/lib/core/locator_service.dart @@ -98,11 +98,11 @@ Future init() async { sl.registerLazySingleton(() => WsManager(wsService: sl())); sl.registerLazySingleton(() => NewMessagesCubit()); sl.registerLazySingleton(() => NotificationsCubit(wsManager: sl())); - sl.registerLazySingleton(() => FeedCubit(feedRepository: sl())); sl.registerLazySingleton(() => ProfileAvatarCubit(profileRepository: sl())); sl.registerLazySingleton(() => ChatsCubit(chatRepository: sl(), wsManager: sl(), profileRepository: sl())); // Cubit factories + sl.registerFactory(() => FeedCubit(feedRepository: sl())); sl.registerFactory(() => RegistrationCubit(registrationRepository: sl())); sl.registerFactory(() => ChatRoomCubit(cardsRepository: sl(), chatRepository: sl(), wsManager: sl())); sl.registerFactory(() => RecipesCubit(feedRepository: sl(), recipeRepository: sl(), aboutRecipeRepository: sl())); @@ -114,7 +114,7 @@ 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())); + sl.registerFactory(() => DiaryCubit(diaryRepository: sl(), glossaryRepository: sl(), profileRepository: sl())); sl.registerFactory(() => AboutDietCubit(dietsRepository: sl(), profileRepository: sl())); sl.registerFactory(() => BlogPostCreateCubit(blogPostRepository: sl())); sl.registerFactory(() => SubCubit(profileRepository: sl())); diff --git a/lib/core/widgets/bottom_sheet_with_blur.dart b/lib/core/widgets/bottom_sheet_with_blur.dart index f7cdf3df..57dc9026 100644 --- a/lib/core/widgets/bottom_sheet_with_blur.dart +++ b/lib/core/widgets/bottom_sheet_with_blur.dart @@ -6,7 +6,7 @@ import 'package:flutter/material.dart'; Future showBottomSheetWithBlur({ required BuildContext context, required Widget child, - double height = 200, + double? height = 200, bool hasBlur = true, bool enableDrag = true, bool userRootNavigator = false, diff --git a/lib/features/chat/models/plan_complain_model.dart b/lib/features/chat/models/plan_complain_model.dart new file mode 100644 index 00000000..63e2af8e --- /dev/null +++ b/lib/features/chat/models/plan_complain_model.dart @@ -0,0 +1,14 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'plan_complain_model.freezed.dart'; +part 'plan_complain_model.g.dart'; + +@freezed +class PlanComplainModel with _$PlanComplainModel { + + factory PlanComplainModel({ + @JsonKey(name: 'description') required String description, + }) = _PlanComplainModel; + + factory PlanComplainModel.fromJson(Map json) => _$PlanComplainModelFromJson(json); +} \ No newline at end of file diff --git a/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart b/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart index bd53c0ac..3c522edb 100644 --- a/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart +++ b/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart @@ -10,6 +10,7 @@ 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/core/websockets/ws_manager.dart'; import 'package:eatvio_app/features/chat/models/chat_message_model.dart'; +import 'package:eatvio_app/features/chat/models/plan_complain_model.dart'; import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.dart'; import 'package:eatvio_app/features/chat/repositories/chat_repository.dart'; import 'package:eatvio_app/features/my_finance/models/card_model.dart'; @@ -92,7 +93,7 @@ class ChatRoomCubit extends Cubit { hasCard: false, ), ); - + if (chatRoom.chosenPlan?.consultantId == chatRoom.users.receiver.id) { await checkCards(); } @@ -209,6 +210,30 @@ class ChatRoomCubit extends Cubit { } } + Future complainAboutConsultation({required String description}) async { + try { + final planId = (state as ChatRoomStateLoaded).chatRoom.chosenPlan?.id; + if (planId == null) return false; + await chatRepository.complainAboutConsultation(id: planId, body: PlanComplainModel(description: description)); + return true; + } catch (e) { + log(e.toString()); + return false; + } + } + + Future finishChosenPlan() async { + try { + final planId = (state as ChatRoomStateLoaded).chatRoom.chosenPlan?.id; + if (planId == null) return false; + await chatRepository.finishChosenPlan(id: planId); + return true; + } catch (e) { + log(e.toString()); + return false; + } + } + @override Future close() async { wsManager.unsubscribeDirectChatChannel(); diff --git a/lib/features/chat/presentation/pages/chat_screen.dart b/lib/features/chat/presentation/pages/chat_screen.dart index 250c86fe..d90aaa93 100644 --- a/lib/features/chat/presentation/pages/chat_screen.dart +++ b/lib/features/chat/presentation/pages/chat_screen.dart @@ -29,7 +29,7 @@ import 'package:visibility_detector/visibility_detector.dart'; class ChatScreen extends StatefulWidget { final String chatId; - final bool sendInitialMessage; + final bool? sendInitialMessage; const ChatScreen({required this.chatId, super.key, required this.sendInitialMessage}); @override @@ -48,7 +48,7 @@ class _ChatScreenState extends State { void initState() { super.initState(); scrollController.addListener(_onScroll); - chatRoomCubit.initChat(widget.chatId, sendInitialMessage: widget.sendInitialMessage); + chatRoomCubit.initChat(widget.chatId, sendInitialMessage: widget.sendInitialMessage ?? false); } _onScroll() { @@ -98,7 +98,7 @@ class _ChatScreenState extends State { BannerType? bannerType; if (chosenPlan == null) { bannerType = null; - } else if (!amIConsultant && !state.hasCard) { + } else if (amIConsultant && !state.hasCard) { bannerType = BannerType.card; } else if (amIConsultant && chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateInCoordination) { bannerType = BannerType.date; @@ -107,6 +107,8 @@ class _ChatScreenState extends State { bannerType = BannerType.payment; } else if (chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateDone) { bannerType = BannerType.collectCharge; + } else if (!(chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateLastDayFinished) && !amIConsultant) { + bannerType = BannerType.finishPlan; } PacketOptions? chosenOption; diff --git a/lib/features/chat/presentation/widgets/complete_plan_bottom_sheet.dart b/lib/features/chat/presentation/widgets/complete_plan_bottom_sheet.dart new file mode 100644 index 00000000..ef950bc9 --- /dev/null +++ b/lib/features/chat/presentation/widgets/complete_plan_bottom_sheet.dart @@ -0,0 +1,102 @@ +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/bottom_sheet_handler_bar.dart'; +import 'package:eatvio_app/core/widgets/custom_outlined_button.dart'; +import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart'; +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +class CompletePlanBottomSheet extends StatefulWidget { + const CompletePlanBottomSheet({super.key, required this.cubit}); + final ChatRoomCubit cubit; + + @override + State createState() => _CompletePlanBottomSheetState(); +} + +class _CompletePlanBottomSheetState extends State { + bool? isFailure; + @override + Widget build(BuildContext context) { + final s = S.of(context); + if (isFailure == true) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox( + height: 8, + ), + const BottomSheetHandlerBar(), + const SizedBox( + height: 20, + ), + Text('Произошла ошибка, попробуйте позже', style: TextStyles.textSMedium.copyWith(color: appTheme.black)), + 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 { + context.pop(); + }, + ), + const SizedBox( + height: 40, + ), + ], + ); + } + 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( + 'Вы уверенны что хотите подтвердить прохождения плана?', + style: TextStyles.textSMedium.copyWith(color: appTheme.black), + textAlign: TextAlign.center, + ), + const SizedBox( + height: 8, + ), + CustomOutlinedButton( + text: s.confirm, + height: 52, + buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), + buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.blueA400)), + onPressed: () async { + final result = await widget.cubit.finishChosenPlan(); + if (result) { + if (context.mounted) { + context.pop(); + return; + } + } + setState(() { + isFailure = true; + }); + }, + ), + const SizedBox( + height: 40, + ), + ], + ), + ); + } +} diff --git a/lib/features/chat/presentation/widgets/notification_banner.dart b/lib/features/chat/presentation/widgets/notification_banner.dart index d4eaf577..8828863f 100644 --- a/lib/features/chat/presentation/widgets/notification_banner.dart +++ b/lib/features/chat/presentation/widgets/notification_banner.dart @@ -1,11 +1,14 @@ 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/bottom_sheet_with_blur.dart'; import 'package:eatvio_app/core/widgets/custom_outlined_button.dart'; import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart'; import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.dart'; import 'package:eatvio_app/features/chat/presentation/pages/payment_screen.dart'; import 'package:eatvio_app/features/chat/presentation/widgets/chose_plan_date_widget.dart'; +import 'package:eatvio_app/features/chat/presentation/widgets/complete_plan_bottom_sheet.dart'; +import 'package:eatvio_app/features/chat/presentation/widgets/plan_complain_bottom_sheet.dart'; import 'package:eatvio_app/features/my_finance/presentation/pages/card_settings.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; @@ -40,7 +43,7 @@ class _NotificationBannerState extends State { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (widget.bannerType != BannerType.collectCharge) + if (widget.bannerType != BannerType.collectCharge && widget.bannerType != BannerType.finishPlan) Padding( padding: const EdgeInsets.only(top: 12, right: 8, left: 12), child: SvgPicture.asset('assets/icons/reversed_exclamation_mark.svg', height: 68, width: 68), @@ -139,6 +142,19 @@ class _NotificationBannerState extends State { ), const SizedBox(height: 8), ], + BannerType.finishPlan => [ + SizedBox( + width: size.width * 0.6, + child: Text('${s.congratsYouHaveFinishedPlan} «${widget.planName}». ${s.pleaseConfirmPlan}', + style: TextStyles.textMMediumAuto.copyWith(color: appTheme.black)), + ), + const SizedBox(height: 8), + SizedBox( + width: size.width * 0.7, + child: Text(s.planWillBeConfirmedAfterThreeDays, style: TextStyles.textSMedium.copyWith(color: appTheme.black)), + ), + const SizedBox(height: 8), + ], }, ), ), @@ -156,7 +172,57 @@ class _NotificationBannerState extends State { GoRouter.of(context).push(CardSettingsScreen.path, extra: widget.chosenPlanId); }, ), - ) + ), + if (widget.bannerType == BannerType.finishPlan) ...[ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: CustomOutlinedButton( + text: s.confirm, + height: 52, + buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), + buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.blueA400)), + onPressed: () async { + final cubit = context.read(); + showBottomSheetWithBlur( + userRootNavigator: true, + context: context, + height: null, + child: CompletePlanBottomSheet( + cubit: cubit, + ), + ); + }, + ), + ), + const SizedBox(height: 12), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: SizedBox( + child: Text(s.youCanComplain, style: TextStyles.textSMedium.copyWith(color: appTheme.black)), + ), + ), + const SizedBox(height: 8), + Align( + alignment: Alignment.centerLeft, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: GestureDetector( + onTap: () { + final cubit = context.read(); + showBottomSheetWithBlur( + userRootNavigator: true, + context: context, + height: null, + child: PlanComplainBottomSheet( + cubit: cubit, + ), + ); + }, + child: Text(s.leaveComplainRequest, style: TextStyles.textSMedium.copyWith(color: appTheme.red))), + ), + ), + const SizedBox(height: 8), + ] ], ), ); @@ -176,4 +242,4 @@ class _StarIcon extends StatelessWidget { } } -enum BannerType { date, card, payment, collectCharge } +enum BannerType { date, card, payment, collectCharge, finishPlan } diff --git a/lib/features/chat/presentation/widgets/plan_complain_bottom_sheet.dart b/lib/features/chat/presentation/widgets/plan_complain_bottom_sheet.dart new file mode 100644 index 00000000..34f500f7 --- /dev/null +++ b/lib/features/chat/presentation/widgets/plan_complain_bottom_sheet.dart @@ -0,0 +1,154 @@ +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/bottom_sheet_handler_bar.dart'; +import 'package:eatvio_app/core/widgets/custom_outlined_button.dart'; +import 'package:eatvio_app/core/widgets/custom_text_form_field.dart'; +import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart'; +import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart'; +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +class PlanComplainBottomSheet extends StatefulWidget { + const PlanComplainBottomSheet({super.key, required this.cubit}); + final ChatRoomCubit cubit; + + @override + State createState() => _PlanComplainBottomSheetState(); +} + +class _PlanComplainBottomSheetState extends State { + final controller = TextEditingController(); + bool? result; + int? textLength; + @override + Widget build(BuildContext context) { + final s = S.of(context); + if (result == true) { + return 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('Жалоба отправлена на рассмотрение. Обычно это занимает до трёх рабочих дней.', style: TextStyles.textSMedium.copyWith(color: appTheme.black)), + 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 { + context.pop(); + }, + ), + const SizedBox( + height: 40, + ), + ], + ); + } else if (result == false) { + return 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('Произошла ошибка, попробуйте позже', style: TextStyles.textSMedium.copyWith(color: appTheme.black)), + 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 { + context.pop(); + }, + ), + const SizedBox( + height: 40, + ), + ], + ); + } + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + 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, + ), + Align(alignment: Alignment.centerLeft, child: Text('Опишите причину жалобы', style: TextStyles.textSMedium.copyWith(color: appTheme.black))), + const SizedBox( + height: 8, + ), + CustomTextFormField( + controller: controller, + hasError: textLength != null && textLength! < 3, + onChanged: (p0) { + setState(() { + textLength = p0.length; + }); + }, + ), + if (textLength != null && textLength! < 3) ...[ + const SizedBox( + height: 4, + ), + ValidationErrorWidget(error: s.atLeast3Symbols), + ], + const SizedBox( + height: 8, + ), + CustomOutlinedButton( + text: s.confirm, + height: 52, + buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), + buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.blueA400)), + onPressed: () async { + if (controller.text.length < 3) { + return; + } + final result = await widget.cubit.complainAboutConsultation(description: controller.text); + setState(() { + this.result = result; + }); + }, + ), + const SizedBox( + height: 40, + ), + ], + ), + ); + } +} diff --git a/lib/features/chat/repositories/chat_repository.dart b/lib/features/chat/repositories/chat_repository.dart index 30c34586..28f80a48 100644 --- a/lib/features/chat/repositories/chat_repository.dart +++ b/lib/features/chat/repositories/chat_repository.dart @@ -6,6 +6,7 @@ import 'package:eatvio_app/features/chat/models/chat_messages_model.dart'; import 'package:eatvio_app/features/chat/models/chat_room_model.dart'; import 'package:eatvio_app/features/chat/models/chat_room_pagination_model.dart'; import 'package:eatvio_app/features/chat/models/payment_model.dart'; +import 'package:eatvio_app/features/chat/models/plan_complain_model.dart'; import 'package:eatvio_app/features/chat/services/chat_service.dart'; import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart'; import 'package:http_parser/http_parser.dart'; @@ -132,4 +133,25 @@ class ChatRepository { rethrow; } } + + Future finishChosenPlan({ + required int id, + }) async { + try { + await chatService.finishChosenPlan(id: id); + } catch (e) { + rethrow; + } + } + + Future complainAboutConsultation({ + required int id, + required PlanComplainModel body, + }) async { + try { + await chatService.complainAboutConsultation(id: id, body: body); + } catch (e) { + rethrow; + } + } } diff --git a/lib/features/chat/services/chat_service.dart b/lib/features/chat/services/chat_service.dart index 2e2a127e..6d4c992e 100644 --- a/lib/features/chat/services/chat_service.dart +++ b/lib/features/chat/services/chat_service.dart @@ -3,6 +3,7 @@ import 'package:eatvio_app/features/chat/models/chat_messages_model.dart'; import 'package:eatvio_app/features/chat/models/chat_room_model.dart'; import 'package:eatvio_app/features/chat/models/chat_room_pagination_model.dart'; import 'package:eatvio_app/features/chat/models/payment_model.dart'; +import 'package:eatvio_app/features/chat/models/plan_complain_model.dart'; import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart'; import 'package:retrofit/retrofit.dart'; @@ -68,4 +69,15 @@ abstract class ChatService { Future getChosenPlan({ @Path() required int id, }); + + @POST('/api/chosen_plans/{id}/confirm_finish') + Future finishChosenPlan({ + @Path() required int id, + }); + + @POST('/api/chosen_plans/{id}/complain') + Future complainAboutConsultation({ + @Path() required int id, + @Body() required PlanComplainModel body, + }); } diff --git a/lib/features/consultations/models/consultation_model.dart b/lib/features/consultations/models/consultation_model.dart index 5e96cb68..4c9464bf 100644 --- a/lib/features/consultations/models/consultation_model.dart +++ b/lib/features/consultations/models/consultation_model.dart @@ -37,8 +37,9 @@ class DiaryModel with _$DiaryModel { } DateTime parseUnformattedDate(String date) { - final dateChunks = date.split('/').map(int.parse).toList(); - return DateTime(dateChunks.last, dateChunks.first, dateChunks[1]); + final dateChunks = date.split('-').map(int.parse).toList(); + + return DateTime(dateChunks.first, dateChunks[1], dateChunks.last); } enum DiaryStateEnum { diff --git a/lib/features/consultations/services/consultation_service.dart b/lib/features/consultations/services/consultation_service.dart index 25719f8a..24881685 100644 --- a/lib/features/consultations/services/consultation_service.dart +++ b/lib/features/consultations/services/consultation_service.dart @@ -8,7 +8,6 @@ part 'consultation_service.g.dart'; abstract class ConsultationService { factory ConsultationService(Dio dio, {String baseUrl}) = _ConsultationService; - @GET('/api/consultations') + @GET('/api/consultations/in_progress') Future getConsultations(); - } diff --git a/lib/features/diary/models/diary_day_model.dart b/lib/features/diary/models/diary_day_model.dart index dbed9b6f..f099987e 100644 --- a/lib/features/diary/models/diary_day_model.dart +++ b/lib/features/diary/models/diary_day_model.dart @@ -23,7 +23,10 @@ enum PortionState { @freezed class DiaryDayWrapperModel with _$DiaryDayWrapperModel { const factory DiaryDayWrapperModel({ - @JsonKey(name: "diary") required DiaryDayModel diary, + @JsonKey( + name: "diary", + ) + required DiaryDayModel diary, }) = _DiaryDayWrapperModel; factory DiaryDayWrapperModel.fromJson(Map json) => _$DiaryDayWrapperModelFromJson(json); diff --git a/lib/features/diary/presentation/bloc/diary_cubit.dart b/lib/features/diary/presentation/bloc/diary_cubit.dart index 3cfe66c7..36934c50 100644 --- a/lib/features/diary/presentation/bloc/diary_cubit.dart +++ b/lib/features/diary/presentation/bloc/diary_cubit.dart @@ -11,6 +11,7 @@ import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart'; import 'package:eatvio_app/features/diary/repositories/diary_repository.dart'; import 'package:eatvio_app/features/glossary/models/glossary_model.dart'; import 'package:eatvio_app/features/glossary/repositories/glossary_repository.dart'; +import 'package:eatvio_app/features/profile/repositories/profile_repository.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class DiaryCubit extends Cubit { @@ -19,8 +20,13 @@ class DiaryCubit extends Cubit { late int selectedMeal; final DiaryRepository diaryRepository; final GlossaryRepository glossaryRepository; + final ProfileRepository profileRepository; int? diaryId; - DiaryCubit({required this.diaryRepository, required this.glossaryRepository}) : super(const DiaryState.loading()) { + DiaryCubit({ + required this.diaryRepository, + required this.glossaryRepository, + required this.profileRepository, + }) : super(const DiaryState.loading()) { today = DateTime.now(); selectedDay = today; diaryRepository.forceFetchStream.listen((_) { @@ -42,6 +48,7 @@ class DiaryCubit extends Cubit { fetchDiary([bool needLoading = false, int? diaryId]) async { this.diaryId ??= diaryId; + await profileRepository.getChosenDiets(); if (needLoading) { emit(const DiaryState.loading()); selectedMeal = 1; @@ -88,7 +95,12 @@ class DiaryCubit extends Cubit { required String wakeUp, required String retire, }) async { - await diaryRepository.finishDiaryDay(diaryId: diaryId, wakeUp: wakeUp, retire: retire, comment: comment); + await diaryRepository.finishDiaryDay( + diaryId: diaryId, + wakeUp: wakeUp, + retire: retire, + comment: comment.isEmpty ? null : comment, + ); await fetchDiary(); } diff --git a/lib/features/diary/presentation/pages/diary_screen.dart b/lib/features/diary/presentation/pages/diary_screen.dart index 1e05e832..7ab05a60 100644 --- a/lib/features/diary/presentation/pages/diary_screen.dart +++ b/lib/features/diary/presentation/pages/diary_screen.dart @@ -79,7 +79,12 @@ class DiaryScreenContent extends StatelessWidget { child: Column( children: [ if (diaryId == null) const CalendarWidget(), - if (state is DiaryStateLoading) const Loader(), + if (state is DiaryStateLoading) ...[ + const SizedBox( + height: 60, + ), + const Loader() + ], if (state is DiaryStateLoaded) WakeupAndRetireWidget( showRetire: state.diary.typeOfDiary.hasChosenPlan, diff --git a/lib/features/diary/presentation/widgets/product_widget.dart b/lib/features/diary/presentation/widgets/product_widget.dart index bc9020cb..0ae071ee 100644 --- a/lib/features/diary/presentation/widgets/product_widget.dart +++ b/lib/features/diary/presentation/widgets/product_widget.dart @@ -34,7 +34,13 @@ class _ProductWidgetState extends State { super.initState(); diaryCubit = context.read(); portionName = widget.portion.portion == 'gr' ? 'гр' : 'шт'; + isEaten = widget.portion.portionState == PortionState.eatenFromDiet ? true : false; + } + @override + void didUpdateWidget(ProductWidget oldWidget) { + super.didUpdateWidget(oldWidget); + portionName = widget.portion.portion == 'gr' ? 'гр' : 'шт'; isEaten = widget.portion.portionState == PortionState.eatenFromDiet ? true : false; } @@ -64,8 +70,9 @@ class _ProductWidgetState extends State { state.diary.typeOfDiary.diaryState == DiaryStateEnum.reviewedByUser; final isDiaryFinished = state.diary.typeOfDiary.diaryState == DiaryStateEnum.finished || state.diary.typeOfDiary.diaryState == DiaryStateEnum.lastDayFinished; - final canEditPortion = !state.isConsultantView && !isDiaryCommented && !isDiaryFinished; + final canEditPortion = !state.isConsultantView && !isDiaryCommented && !isDiaryFinished && !widget.portion.fromDiet; final consultantComments = state.consultantReview?.consultantComments; + final canMarkAsEaten = !state.isConsultantView && !isDiaryCommented && !isDiaryFinished; PortionComment? portionComment; if (consultantComments != null) { portionComment = consultantComments.portionsComments.firstWhereOrNull((elem) => elem.portionId == widget.portion.id); @@ -108,7 +115,7 @@ class _ProductWidgetState extends State { }); }, child: SvgPicture.asset('assets/icons/edit.svg')); - } else if (!isDiaryCommented) { + } else if (!isDiaryCommented && !widget.portion.fromDiet) { trailing = const Icon(Icons.more_vert_rounded); } return Column( @@ -121,24 +128,26 @@ class _ProductWidgetState extends State { scale: 1.2, child: Checkbox( value: isEaten, - onChanged: (value) { - setState(() { - isEaten = value!; - }); - if (isEaten) { - diaryCubit.markEaten( - diaryId: state.diary.id, - mealId: state.selectedMeal, - portionId: widget.portion.id, - ); - } else { - diaryCubit.unmarkEaten( - diaryId: state.diary.id, - mealId: state.selectedMeal, - portionId: widget.portion.id, - ); - } - }, + onChanged: canMarkAsEaten + ? (value) { + setState(() { + isEaten = value!; + }); + if (isEaten) { + diaryCubit.markEaten( + diaryId: state.diary.id, + mealId: state.selectedMeal, + portionId: widget.portion.id, + ); + } else { + diaryCubit.unmarkEaten( + diaryId: state.diary.id, + mealId: state.selectedMeal, + portionId: widget.portion.id, + ); + } + } + : null, activeColor: appTheme.blueA400, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(4), diff --git a/lib/features/feed/models/plan_pagination_model.dart b/lib/features/feed/models/plan_pagination_model.dart index 63e283c6..8723f490 100644 --- a/lib/features/feed/models/plan_pagination_model.dart +++ b/lib/features/feed/models/plan_pagination_model.dart @@ -30,9 +30,9 @@ class PlanModel with _$PlanModel { @JsonKey(name: "type", fromJson: planTypeFromJson) required PlanType type, @JsonKey(name: "description_md") required String details, @JsonKey(name: "packets_count") required int packetsCount, - @JsonKey(name: "packet1_options") PacketOptions? packet1Options, - @JsonKey(name: "packet2_options") PacketOptions? packet2Options, - @JsonKey(name: "packet3_options") PacketOptions? packet3Options, + @JsonKey(name: "packet1_options", fromJson: parseOption) PacketOptions? packet1Options, + @JsonKey(name: "packet2_options", fromJson: parseOption) PacketOptions? packet2Options, + @JsonKey(name: "packet3_options", fromJson: parseOption) PacketOptions? packet3Options, @JsonKey(name: "author") required Author author, @JsonKey(name: "total_likes") required int totalLikes, @JsonKey(name: "total_views") required int totalViews, @@ -46,6 +46,13 @@ class PlanModel with _$PlanModel { factory PlanModel.fromJson(Map json) => _$PlanModelFromJson(json); } +PacketOptions? parseOption(json) { + if (json is Map) { + return PacketOptions.fromJson(json); + } + return null; +} + PlanType planTypeFromJson(dynamic json) { if (json is List) { return const PlanType(fit: false, slim: false, behavior: false, wellness: false); diff --git a/lib/features/internet_connection_checker/internet_connection_checker_screen.dart b/lib/features/internet_connection_checker/internet_connection_checker_screen.dart index a8e65420..0f7f6e2c 100644 --- a/lib/features/internet_connection_checker/internet_connection_checker_screen.dart +++ b/lib/features/internet_connection_checker/internet_connection_checker_screen.dart @@ -68,7 +68,7 @@ class _InternetConnectionCheckerScreenState extends State { const SizedBox(height: 8), PlanProps( planDuration: plan.plan.duration, - planPrice: chosenOption?.price ?? 0, + planPrice: chosenOption?.price.toDouble() ?? 0, planStatus: planStatus), const SizedBox(height: 8), PlanInfo( diff --git a/lib/features/plans/enums/chosen_plan_state_enum.dart b/lib/features/plans/enums/chosen_plan_state_enum.dart index 5bb92efd..308df73c 100644 --- a/lib/features/plans/enums/chosen_plan_state_enum.dart +++ b/lib/features/plans/enums/chosen_plan_state_enum.dart @@ -45,6 +45,7 @@ enum ChosenPlanStateEnum { bool isStateAfterPaidState() { switch (this) { + case ChosenPlanStateEnum.stateCanceledByTheInitiator: case ChosenPlanStateEnum.stateInited: case ChosenPlanStateEnum.stateInCoordination: case ChosenPlanStateEnum.stateStartDateHasBeenSet: diff --git a/lib/features/welcome/presentation/pages/welcome_screen.dart b/lib/features/welcome/presentation/pages/welcome_screen.dart index 6ede4081..df82f23b 100644 --- a/lib/features/welcome/presentation/pages/welcome_screen.dart +++ b/lib/features/welcome/presentation/pages/welcome_screen.dart @@ -42,7 +42,7 @@ class _WelcomeScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - const SizedBox(height: 40), + const SizedBox(height: 80), SvgPicture.asset('assets/images/logo_welcome.svg'), const Spacer(), Padding( @@ -70,7 +70,7 @@ class _WelcomeScreenState extends State { style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, - height: 1, + height: 1.15, ), textAlign: TextAlign.center, ), diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 84b4d90e..dc980241 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -379,6 +379,9 @@ class MessageLookup extends MessageLookupByLibrary { "Нарушение законодательства", ), "lbl_english": MessageLookupByLibrary.simpleMessage("English"), + "leaveComplainRequest": MessageLookupByLibrary.simpleMessage( + "Подать жалобу", + ), "light_type": MessageLookupByLibrary.simpleMessage("Легкая"), "light_type_desc": MessageLookupByLibrary.simpleMessage( "С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками.", @@ -501,6 +504,9 @@ class MessageLookup extends MessageLookupByLibrary { "Физическая форма", ), "place_of_living": MessageLookupByLibrary.simpleMessage("Место проживания"), + "planWillBeConfirmedAfterThreeDays": MessageLookupByLibrary.simpleMessage( + "Если вы не подтвердите, то план через три дня подтвердится автоматически", + ), "plan_author": MessageLookupByLibrary.simpleMessage("Автор плана"), "plan_desc": MessageLookupByLibrary.simpleMessage("Описание плана"), "plan_desc_example": MessageLookupByLibrary.simpleMessage( @@ -513,6 +519,9 @@ class MessageLookup extends MessageLookupByLibrary { "plan_seller": MessageLookupByLibrary.simpleMessage("Продавец плана"), "plan_type": MessageLookupByLibrary.simpleMessage("Тип плана"), "plans_complete": MessageLookupByLibrary.simpleMessage("Пройденные планы"), + "pleaseConfirmPlan": MessageLookupByLibrary.simpleMessage( + "Просим вас подтвердить прохождение плана", + ), "please_try_again_later": MessageLookupByLibrary.simpleMessage( "Пожалуйста, попробуйте еще раз позже", ), @@ -704,6 +713,9 @@ class MessageLookup extends MessageLookupByLibrary { "wrong_credentials": MessageLookupByLibrary.simpleMessage( "Неверные данные пользователя", ), + "youCanComplain": MessageLookupByLibrary.simpleMessage( + "Если вас что-то не устроило, вы можете подать жалобу", + ), "youShureYouWantToCancelOrder": MessageLookupByLibrary.simpleMessage( "Вы уверены, что хотите отменить заказ?", ), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 92f85b98..8da944a3 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -3184,6 +3184,46 @@ class S { args: [], ); } + + /// `Просим вас подтвердить прохождение плана` + String get pleaseConfirmPlan { + return Intl.message( + 'Просим вас подтвердить прохождение плана', + name: 'pleaseConfirmPlan', + desc: '', + args: [], + ); + } + + /// `Если вы не подтвердите, то план через три дня подтвердится автоматически` + String get planWillBeConfirmedAfterThreeDays { + return Intl.message( + 'Если вы не подтвердите, то план через три дня подтвердится автоматически', + name: 'planWillBeConfirmedAfterThreeDays', + desc: '', + args: [], + ); + } + + /// `Если вас что-то не устроило, вы можете подать жалобу` + String get youCanComplain { + return Intl.message( + 'Если вас что-то не устроило, вы можете подать жалобу', + name: 'youCanComplain', + desc: '', + args: [], + ); + } + + /// `Подать жалобу` + String get leaveComplainRequest { + return Intl.message( + 'Подать жалобу', + name: 'leaveComplainRequest', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 7d09e039..d6038cb7 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -381,6 +381,10 @@ "atLeast3Symbols": "Минимум 3 символа", "chosePlanType": "Выберите тип плана", "minPlanPrice": "Минимальная сумма 1200₽", - "addAtLeastOneItem": "Добавьте хотя бы одину опцию" + "addAtLeastOneItem": "Добавьте хотя бы одину опцию", + "pleaseConfirmPlan": "Просим вас подтвердить прохождение плана", + "planWillBeConfirmedAfterThreeDays": "Если вы не подтвердите, то план через три дня подтвердится автоматически", + "youCanComplain": "Если вас что-то не устроило, вы можете подать жалобу", + "leaveComplainRequest": "Подать жалобу" } diff --git a/pubspec.yaml b/pubspec.yaml index dfb0838a..46ddf5d1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: eatvio_app description: A new Flutter project. -version: 1.0.50+6 +version: 1.0.51+6 publish_to: 'none' environment: From 2ab148d0d0f32c64ee408d515fa3639d550ec6af Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Wed, 30 Apr 2025 17:26:34 +0300 Subject: [PATCH 18/26] feat(menu): add popup to recipe, diet, plan --- lib/core/widgets/custom_popup.dart | 11 +++++- .../presentation/pages/about_diet_screen.dart | 31 ++++----------- .../presentation/widgets/diets_widget.dart | 36 ++++++------------ .../presentation/widgets/plan_widget.dart | 38 +++++++------------ .../presentation/widgets/recipes_widget.dart | 36 ++++++------------ .../presentation/pages/about_plan_screen.dart | 31 ++++----------- .../presentation/about_recipe_screen.dart | 30 ++++++--------- 7 files changed, 74 insertions(+), 139 deletions(-) diff --git a/lib/core/widgets/custom_popup.dart b/lib/core/widgets/custom_popup.dart index 967f6fee..711995f2 100644 --- a/lib/core/widgets/custom_popup.dart +++ b/lib/core/widgets/custom_popup.dart @@ -90,10 +90,16 @@ class CustomPopup extends StatelessWidget { addItem('assets/icons/edit_paint.svg', 'Редактировать', () { if (complainedContentType == ComplainedContent.posts) { GoRouter.of(context).pushNamed('blogPostCreateScreen', extra: feedBlogPostModel); + } else { + showInfoToast('В разработке'); + return; } }); if (complainedContentType == ComplainedContent.plan || complainedContentType == ComplainedContent.diet) { - addItem('assets/icons/remove_from_public.svg', 'Снять с публикации', () {}); + addItem('assets/icons/remove_from_public.svg', 'Снять с публикации', () { + showInfoToast('В разработке'); + return; + }); } addItem('assets/icons/red_trash.svg', 'Удалить', () async { if (complainedContentType == ComplainedContent.posts) { @@ -104,6 +110,9 @@ class CustomPopup extends StatelessWidget { return; } context.read().loadFeedBlogPostList(); + } else { + showInfoToast('В разработке'); + return; } }); } else { diff --git a/lib/features/diets/presentation/pages/about_diet_screen.dart b/lib/features/diets/presentation/pages/about_diet_screen.dart index 0ca2c959..d4a07038 100644 --- a/lib/features/diets/presentation/pages/about_diet_screen.dart +++ b/lib/features/diets/presentation/pages/about_diet_screen.dart @@ -7,6 +7,7 @@ import 'package:eatvio_app/core/enums/product_type.dart'; import 'package:eatvio_app/core/utils/localization_utils.dart'; import 'package:eatvio_app/core/widgets/bookmark_button.dart'; import 'package:eatvio_app/core/widgets/custom_divider.dart'; +import 'package:eatvio_app/core/widgets/custom_popup.dart'; import 'package:eatvio_app/core/widgets/data_error_widget.dart'; import 'package:eatvio_app/core/widgets/info_row.dart'; import 'package:eatvio_app/core/widgets/loader.dart'; @@ -317,31 +318,13 @@ class _AboutDietScreenState extends State { style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), actions: [ - PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - path: 'diet/${widget.diet.id}', - complainedContentType: ComplainedContent.diet, - id: widget.diet.id, - ), - anchor: const Aligned( - shiftToWithinBound: AxisFlag(x: true, y: true), - follower: Alignment.topRight, - target: Alignment.bottomRight, - ), - child: IconButton( - onPressed: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - icon: SvgPicture.asset( - 'assets/icons/more_button.svg', - width: 20, - height: 20, - ), - ), + CustomPopup( + isAuthor: widget.diet.author.authorIsMe, + path: 'diet/${widget.diet.id}', + complainedContentType: ComplainedContent.diet, + id: widget.diet.id, ), + const SizedBox(width: 16), ], ), body: SingleChildScrollView( diff --git a/lib/features/feed/presentation/widgets/diets_widget.dart b/lib/features/feed/presentation/widgets/diets_widget.dart index fa9dce69..87091ad8 100644 --- a/lib/features/feed/presentation/widgets/diets_widget.dart +++ b/lib/features/feed/presentation/widgets/diets_widget.dart @@ -3,9 +3,9 @@ import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/enums/complained_content.dart'; import 'package:eatvio_app/core/enums/product_type.dart'; import 'package:eatvio_app/core/widgets/bookmark_button.dart'; +import 'package:eatvio_app/core/widgets/custom_popup.dart'; import 'package:eatvio_app/core/widgets/info_row.dart'; import 'package:eatvio_app/core/widgets/feed_icon_button.dart'; -import 'package:eatvio_app/features/diets/presentation/widgets/more_options.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart'; import 'package:eatvio_app/core/theme/custom_text_style.dart'; @@ -13,7 +13,6 @@ import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_html/flutter_html.dart'; -import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:go_router/go_router.dart'; import 'package:share_plus/share_plus.dart'; @@ -83,28 +82,17 @@ class _DietsWidgetState extends State { Positioned( right: 12, top: 12, - child: PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - path: 'diet/${widget.diet.id}', - complainedContentType: ComplainedContent.diet, - id: widget.diet.id, - ), - anchor: const Aligned(shiftToWithinBound: AxisFlag(x: true, y: true), follower: Alignment.topRight, target: Alignment.bottomRight), - child: InkWell( - onTap: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - child: SizedBox( - width: 32, - height: 24, - child: Material( - color: Colors.white, - borderRadius: BorderRadius.circular(4), - child: const Icon(Icons.more_horiz), - ), + child: SizedBox( + width: 32, + height: 24, + child: Material( + color: Colors.white, + borderRadius: BorderRadius.circular(4), + child: CustomPopup( + isAuthor: widget.diet.author.authorIsMe, + path: 'diet/${widget.diet.id}', + id: widget.diet.id, + complainedContentType: ComplainedContent.diet, ), ), ), diff --git a/lib/features/feed/presentation/widgets/plan_widget.dart b/lib/features/feed/presentation/widgets/plan_widget.dart index 2951d90a..608d647b 100644 --- a/lib/features/feed/presentation/widgets/plan_widget.dart +++ b/lib/features/feed/presentation/widgets/plan_widget.dart @@ -3,10 +3,10 @@ import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/enums/complained_content.dart'; import 'package:eatvio_app/core/enums/product_type.dart'; import 'package:eatvio_app/core/widgets/bookmark_button.dart'; +import 'package:eatvio_app/core/widgets/custom_popup.dart'; import 'package:eatvio_app/core/widgets/info_row.dart'; import 'package:eatvio_app/core/widgets/feed_icon_button.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart'; -import 'package:eatvio_app/features/diets/presentation/widgets/more_options.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart'; import 'package:eatvio_app/core/theme/custom_text_style.dart'; @@ -16,7 +16,6 @@ import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_html/flutter_html.dart'; -import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:go_router/go_router.dart'; import 'package:share_plus/share_plus.dart'; @@ -24,6 +23,7 @@ import 'package:share_plus/share_plus.dart'; class PlanWidget extends StatefulWidget { final PlanModel plan; final int index; + const PlanWidget({ super.key, required this.plan, @@ -87,28 +87,18 @@ class _PlanWidgetState extends State { Positioned( right: 12, top: 12, - child: PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - path: 'plan/${widget.plan.id}', - complainedContentType: ComplainedContent.plan, - id: widget.plan.id, - ), - anchor: const Aligned(shiftToWithinBound: AxisFlag(x: true, y: true), follower: Alignment.topRight, target: Alignment.bottomRight), - child: InkWell( - onTap: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - child: SizedBox( - width: 32, - height: 24, - child: Material( - color: Colors.white, - borderRadius: BorderRadius.circular(4), - child: const Icon(Icons.more_horiz), - ), + child: SizedBox( + width: 32, + height: 24, + child: Material( + color: Colors.white, + borderRadius: BorderRadius.circular(4), + child: CustomPopup( + isAuthor: widget.plan.author.authorIsMe, + path: 'plan/${widget.plan.id}', + id: widget.plan.id, + slug: widget.plan.slug, + complainedContentType: ComplainedContent.plan, ), ), ), diff --git a/lib/features/feed/presentation/widgets/recipes_widget.dart b/lib/features/feed/presentation/widgets/recipes_widget.dart index 877aa19b..39ca3ee1 100644 --- a/lib/features/feed/presentation/widgets/recipes_widget.dart +++ b/lib/features/feed/presentation/widgets/recipes_widget.dart @@ -6,8 +6,8 @@ import 'package:eatvio_app/core/enums/complained_content.dart'; import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/utils/localization_utils.dart'; import 'package:eatvio_app/core/widgets/bookmark_button.dart'; +import 'package:eatvio_app/core/widgets/custom_popup.dart'; import 'package:eatvio_app/core/widgets/feed_icon_button.dart'; -import 'package:eatvio_app/features/diets/presentation/widgets/more_options.dart'; import 'package:eatvio_app/features/feed/models/recipes_model.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart'; @@ -16,7 +16,6 @@ import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_html/flutter_html.dart'; -import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:go_router/go_router.dart'; import 'package:share_plus/share_plus.dart'; @@ -117,28 +116,17 @@ class _RecipesWidgetState extends State { Positioned( right: 12, top: 12, - child: PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - complainedContentType: ComplainedContent.recipe, - path: 'recipe/${widget.recipe.slug}', - slug: widget.recipe.slug, - ), - anchor: const Aligned(shiftToWithinBound: AxisFlag(x: true, y: true), follower: Alignment.topRight, target: Alignment.bottomRight), - child: InkWell( - onTap: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - child: SizedBox( - width: 32, - height: 24, - child: Material( - color: Colors.white, - borderRadius: BorderRadius.circular(4), - child: const Icon(Icons.more_horiz), - ), + child: SizedBox( + width: 32, + height: 24, + child: Material( + color: Colors.white, + borderRadius: BorderRadius.circular(4), + child: CustomPopup( + isAuthor: widget.recipe.author.authorIsMe, + path: 'recipe/${widget.recipe.slug}', + slug: widget.recipe.slug, + complainedContentType: ComplainedContent.recipe, ), ), ), diff --git a/lib/features/plans/presentation/pages/about_plan_screen.dart b/lib/features/plans/presentation/pages/about_plan_screen.dart index 2935310f..c4958042 100644 --- a/lib/features/plans/presentation/pages/about_plan_screen.dart +++ b/lib/features/plans/presentation/pages/about_plan_screen.dart @@ -6,6 +6,7 @@ import 'package:eatvio_app/core/enums/complained_content.dart'; import 'package:eatvio_app/core/enums/product_type.dart'; import 'package:eatvio_app/core/utils/localization_utils.dart'; import 'package:eatvio_app/core/widgets/bookmark_button.dart'; +import 'package:eatvio_app/core/widgets/custom_popup.dart'; import 'package:eatvio_app/core/widgets/data_error_widget.dart'; import 'package:eatvio_app/core/widgets/info_row.dart'; import 'package:eatvio_app/core/widgets/loader.dart'; @@ -112,31 +113,13 @@ class _AboutPlanScreenState extends State { style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), actions: [ - PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - complainedContentType: ComplainedContent.plan, - path: 'plan/${widget.plan.id}', - slug: widget.plan.slug, - ), - anchor: const Aligned( - shiftToWithinBound: AxisFlag(x: true, y: true), - follower: Alignment.topRight, - target: Alignment.bottomRight, - ), - child: IconButton( - onPressed: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - icon: SvgPicture.asset( - 'assets/icons/more_button.svg', - width: 20, - height: 20, - ), - ), + CustomPopup( + isAuthor: widget.plan.author.authorIsMe, + path: 'plan/${widget.plan.id}', + complainedContentType: ComplainedContent.plan, + slug: widget.plan.slug, ), + const SizedBox(width: 16), ], ), body: const Loader(), diff --git a/lib/features/recipe/presentation/about_recipe_screen.dart b/lib/features/recipe/presentation/about_recipe_screen.dart index 9139e094..cc65a0c2 100644 --- a/lib/features/recipe/presentation/about_recipe_screen.dart +++ b/lib/features/recipe/presentation/about_recipe_screen.dart @@ -8,6 +8,7 @@ import 'package:eatvio_app/core/enums/product_type.dart'; import 'package:eatvio_app/core/utils/localization_utils.dart'; import 'package:eatvio_app/core/utils/toasts.dart'; import 'package:eatvio_app/core/widgets/bookmark_button.dart'; +import 'package:eatvio_app/core/widgets/custom_popup.dart'; import 'package:eatvio_app/core/widgets/data_error_widget.dart'; import 'package:eatvio_app/core/widgets/info_row.dart'; import 'package:eatvio_app/core/widgets/loader.dart'; @@ -54,6 +55,7 @@ class _AboutRecipeScreenState extends State { final textEditingController = TextEditingController(); bool isMoreOptionsVisible = false; bool _jumped = false; + @override void initState() { super.initState(); @@ -142,8 +144,10 @@ class _AboutRecipeScreenState extends State { keyboardType: TextInputType.multiline, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w400, color: Colors.black, height: 1.2), decoration: InputDecoration( - isDense: false, // убираем true, чтобы padding учитывался корректно - contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), // вертикальный паддинг увеличен + isDense: false, + // убираем true, чтобы padding учитывался корректно + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), + // вертикальный паддинг увеличен hintText: 'Комментарий', hintStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.w400, height: 1.2, color: Color(0xFFAEAEB2)), filled: true, @@ -211,23 +215,13 @@ class _AboutRecipeScreenState extends State { style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), actions: [ - PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - complainedContentType: ComplainedContent.recipe, - path: 'recipe/${widget.recipe.slug}', - slug: widget.recipe.slug, - ), - anchor: const Aligned(shiftToWithinBound: AxisFlag(x: true, y: true), follower: Alignment.topRight, target: Alignment.bottomRight), - child: IconButton( - onPressed: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - icon: SvgPicture.asset('assets/icons/more_button.svg', width: 20, height: 20), - ), + CustomPopup( + isAuthor: widget.recipe.author.authorIsMe, + path: 'recipe/${widget.recipe.slug}', + complainedContentType: ComplainedContent.recipe, + slug: widget.recipe.slug, ), + const SizedBox(width: 16), ], ), body: SingleChildScrollView( From 5e6cdca9c35f2db0e3f66774f1af9da40bfbc3f1 Mon Sep 17 00:00:00 2001 From: Username1548 Date: Wed, 30 Apr 2025 21:32:05 +0700 Subject: [PATCH 19/26] changed base url --- lib/core/const.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/const.dart b/lib/core/const.dart index 4f38a54e..31c47e9c 100644 --- a/lib/core/const.dart +++ b/lib/core/const.dart @@ -1,5 +1,5 @@ -// String baseUrl = 'https://eatvio.ru'; -String baseUrl = 'https://dev2.eatvio.com'; +String baseUrl = 'https://eatvio.ru'; +// String baseUrl = 'https://dev2.eatvio.com'; const List durationValues = [7, 14, 21]; From 849e3cde721ea37d77fdb5f1afa69b84a747929b Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Wed, 30 Apr 2025 14:34:17 +0000 Subject: [PATCH 20/26] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20.gitea/workflows/test.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index a98a1561..8c247e87 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -41,6 +41,8 @@ jobs: wget https://dl.google.com/android/repository/commandlinetools-linux-7583922_latest.zip -O commandlinetools.zip unzip commandlinetools.zip mkdir -p ${{ env.my_local_cache_dir }}Android/Sdk/cmdline-tools/latest + rm -rf ${{ env.my_local_cache_dir }}Android/Sdk/cmdline-tools/latest + mkdir -p ${{ env.my_local_cache_dir }}Android/Sdk/cmdline-tools/latest mv cmdline-tools/* ${{ env.my_local_cache_dir }}Android/Sdk/cmdline-tools/latest/ export ANDROID_SDK_ROOT=${{ env.my_local_cache_dir }}Android/Sdk export PATH=$PATH:${{ env.my_local_cache_dir }}Android/Sdk/cmdline-tools/latest/bin From 60d244aac2e9a818cde727be6ba1e23165fff53f Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Wed, 30 Apr 2025 18:09:19 +0300 Subject: [PATCH 21/26] fix(support): link --- lib/features/support/presentation/pages/support_screen.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/support/presentation/pages/support_screen.dart b/lib/features/support/presentation/pages/support_screen.dart index d4bef652..b550040f 100644 --- a/lib/features/support/presentation/pages/support_screen.dart +++ b/lib/features/support/presentation/pages/support_screen.dart @@ -102,7 +102,7 @@ class SupportScreen extends StatelessWidget { ), const SizedBox(height: 12), InkWell( - onTap: () async => await launchUrl(Uri.parse('$baseUrl/public_offer.pdf')), + onTap: () async => await launchUrl(Uri.parse('$baseUrl/docs/public_offer.pdf')), borderRadius: BorderRadius.circular(16), child: const Padding( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4), From acc88470f1139478bf95c5145901970cab72de55 Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Mon, 5 May 2025 15:47:25 +0300 Subject: [PATCH 22/26] fix(welcomeScreen): remove emoji --- lib/features/welcome/presentation/pages/welcome_screen.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/features/welcome/presentation/pages/welcome_screen.dart b/lib/features/welcome/presentation/pages/welcome_screen.dart index df82f23b..ade59f8c 100644 --- a/lib/features/welcome/presentation/pages/welcome_screen.dart +++ b/lib/features/welcome/presentation/pages/welcome_screen.dart @@ -56,7 +56,7 @@ class _WelcomeScreenState extends State { children: [ const SizedBox(height: 20), const Text( - 'Добро пожаловать\nв EATVIO! 🚀', + 'Добро пожаловать', style: TextStyle( fontSize: 28, fontWeight: FontWeight.w800, @@ -66,7 +66,7 @@ class _WelcomeScreenState extends State { ), const SizedBox(height: 16), const Text( - 'Это первая версия приложения, и мы активно работаем над его улучшением. Если вы заметите ошибку или у вас есть идея — пожалуйста, сообщите нам! Ваша помощь бесценна ❤️', + 'Это первая версия приложения, и мы активно работаем над его улучшением. Если вы заметите ошибку или у вас есть идея — пожалуйста, сообщите нам! Ваша помощь бесценна', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, From 5852a676548f99b36a44a12ad9664c577cc16c92 Mon Sep 17 00:00:00 2001 From: Username1548 Date: Tue, 6 May 2025 17:20:36 +0700 Subject: [PATCH 23/26] fix(consultations): fixed consultations screen --- .../chat/presentation/pages/chat_screen.dart | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/features/chat/presentation/pages/chat_screen.dart b/lib/features/chat/presentation/pages/chat_screen.dart index d90aaa93..b20646b9 100644 --- a/lib/features/chat/presentation/pages/chat_screen.dart +++ b/lib/features/chat/presentation/pages/chat_screen.dart @@ -86,12 +86,11 @@ class _ChatScreenState extends State { _buildChat(BuildContext context, ChatRoomStateLoaded state) { final chosenPlan = state.chatRoom.chosenPlan; final amIConsultant = chosenPlan?.consultantId == state.chatRoom.users.sender.id; - final showUserDetailsButton = amIConsultant && - [ - ChosenPlanStateEnum.stateStartDateHasBeenSet, - ChosenPlanStateEnum.statePendingPayment, - ChosenPlanStateEnum.stateInCoordination, - ].contains(chosenPlan?.chosenPlanState); + final showUserDetailsButton = [ + ChosenPlanStateEnum.stateStartDateHasBeenSet, + ChosenPlanStateEnum.statePendingPayment, + ChosenPlanStateEnum.stateInCoordination, + ].contains(chosenPlan?.chosenPlanState); final showAboutOrderButton = state.chatRoom.chosenPlan != null && state.chatRoom.chosenPlan!.chosenPlanState != ChosenPlanStateEnum.stateCanceledByTheInitiator; @@ -107,7 +106,7 @@ class _ChatScreenState extends State { bannerType = BannerType.payment; } else if (chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateDone) { bannerType = BannerType.collectCharge; - } else if (!(chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateLastDayFinished) && !amIConsultant) { + } else if (chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateLastDayFinished && !amIConsultant) { bannerType = BannerType.finishPlan; } @@ -278,7 +277,7 @@ class _ChatScreenState extends State { itemCount: state.chatMessages.length + (state.isLoading ? 1 : 0), ), const SizedBox(height: 16), - if (!showUserDetailsButton) ...[ + if (showUserDetailsButton) ...[ // OrderCard( // plan: chosenPlan.plan, // isCustomerView: !amIConsultant, From c790214a26d0d7173f595797d52e702e62eee7d6 Mon Sep 17 00:00:00 2001 From: Username1548 Date: Tue, 6 May 2025 17:22:45 +0700 Subject: [PATCH 24/26] fix(chat): fixed chat screen --- lib/features/consultations/services/consultation_service.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/consultations/services/consultation_service.dart b/lib/features/consultations/services/consultation_service.dart index 24881685..e5236e0f 100644 --- a/lib/features/consultations/services/consultation_service.dart +++ b/lib/features/consultations/services/consultation_service.dart @@ -8,6 +8,6 @@ part 'consultation_service.g.dart'; abstract class ConsultationService { factory ConsultationService(Dio dio, {String baseUrl}) = _ConsultationService; - @GET('/api/consultations/in_progress') + @GET('/api/consultations') Future getConsultations(); } From b2d9e808feead5153d428e49c0b417632988939f Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Wed, 7 May 2025 12:48:52 +0300 Subject: [PATCH 25/26] feat(more_options): delete recipe, diet, plans, move to draft diets and plans --- lib/core/widgets/custom_popup.dart | 58 +++++++++++++++-- .../presentation/diet_preview_screen.dart | 32 +++------- .../presentation/pages/about_diet_screen.dart | 62 ++++--------------- .../diets/repositories/diet_repository.dart | 8 +++ lib/features/diets/services/diet_service.dart | 5 ++ .../bloc/diets_cubit/diets_cubit.dart | 10 +++ .../bloc/recipes_cubit/recipes_cubit.dart | 17 +++-- .../presentation/cubit/my_plans_cubit.dart | 9 +++ .../repositories/my_diets_repository.dart | 5 +- .../my_plans/services/my_plans_service.dart | 10 +-- .../presentation/pages/about_plan_screen.dart | 62 ++++--------------- .../presentation/about_recipe_screen.dart | 46 ++++---------- .../repositories/about_recipe_repository.dart | 8 +++ .../recipe/services/about_recipe_service.dart | 5 ++ 14 files changed, 163 insertions(+), 174 deletions(-) diff --git a/lib/core/widgets/custom_popup.dart b/lib/core/widgets/custom_popup.dart index 711995f2..a4167f11 100644 --- a/lib/core/widgets/custom_popup.dart +++ b/lib/core/widgets/custom_popup.dart @@ -7,7 +7,11 @@ import 'package:eatvio_app/core/widgets/show_error_dialog.dart'; import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_cubit.dart'; import 'package:eatvio_app/features/complaining/presentation/complaining_bottom_sheet.dart'; import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart'; +import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart'; +import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart'; +import 'package:eatvio_app/features/my_diets/presentation/cubit/my_diets_cubit.dart'; +import 'package:eatvio_app/features/my_plans/presentation/cubit/my_plans_cubit.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -96,23 +100,65 @@ class CustomPopup extends StatelessWidget { } }); if (complainedContentType == ComplainedContent.plan || complainedContentType == ComplainedContent.diet) { - addItem('assets/icons/remove_from_public.svg', 'Снять с публикации', () { - showInfoToast('В разработке'); - return; + addItem('assets/icons/remove_from_public.svg', 'Снять с публикации', () async { + if (complainedContentType == ComplainedContent.diet) { + final result = await sl().unpublishDiet(id!); + if (!context.mounted) return; + context.read().loadFeedBlogPostList(); + if (!result) { + showErrorDialog(context, title: S.current.could_not_delete_blog_post); + return; + } + showSuccessToast('Диета перенесена в архив, обновите ленту'); + } else if (complainedContentType == ComplainedContent.plan) { + final result = await sl().unpublish(slug!); + if (!context.mounted) return; + context.read().loadFeedBlogPostList(); + if (!result) { + showErrorDialog(context, title: S.current.could_not_delete_blog_post); + return; + } + showSuccessToast('План перенесен в архив, обновите ленту'); + } }); } addItem('assets/icons/red_trash.svg', 'Удалить', () async { if (complainedContentType == ComplainedContent.posts) { final result = await sl().deleteBlogPost(id!); if (!context.mounted) return; + context.read().loadFeedBlogPostList(); if (!result) { showErrorDialog(context, title: S.current.could_not_delete_blog_post); return; } + showSuccessToast('Пост удален, обновите ленту'); + } else if (complainedContentType == ComplainedContent.recipe) { + final result = await sl().deleteRecipe(slug!); + if (!context.mounted) return; context.read().loadFeedBlogPostList(); - } else { - showInfoToast('В разработке'); - return; + if (!result) { + showErrorDialog(context, title: S.current.could_not_delete_blog_post); + return; + } + showSuccessToast('Рецепт удален, обновите ленту'); + } else if (complainedContentType == ComplainedContent.diet) { + final result = await sl().deleteDiet(id!); + if (!context.mounted) return; + context.read().loadFeedBlogPostList(); + if (!result) { + showErrorDialog(context, title: S.current.could_not_delete_blog_post); + return; + } + showSuccessToast('Диета удалена, обновите ленту'); + } else if (complainedContentType == ComplainedContent.plan) { + final result = await sl().deletePlan(slug!); + if (!context.mounted) return; + context.read().loadFeedBlogPostList(); + if (!result) { + showErrorDialog(context, title: S.current.could_not_delete_blog_post); + return; + } + showSuccessToast('План удален, обновите ленту'); } }); } else { diff --git a/lib/features/create_diet/presentation/diet_preview_screen.dart b/lib/features/create_diet/presentation/diet_preview_screen.dart index 759a9ad3..884c283d 100644 --- a/lib/features/create_diet/presentation/diet_preview_screen.dart +++ b/lib/features/create_diet/presentation/diet_preview_screen.dart @@ -1,24 +1,24 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; +import 'package:eatvio_app/core/enums/complained_content.dart'; import 'package:eatvio_app/core/enums/product_type.dart'; import 'package:eatvio_app/core/utils/localization_utils.dart'; import 'package:eatvio_app/core/utils/string_extentions.dart'; import 'package:eatvio_app/core/widgets/bookmark_button.dart'; import 'package:eatvio_app/core/widgets/custom_divider.dart'; +import 'package:eatvio_app/core/widgets/custom_popup.dart'; import 'package:eatvio_app/core/widgets/info_row.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart'; import 'package:eatvio_app/core/widgets/cpfc_container.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart'; import 'package:eatvio_app/core/widgets/main_background_bouble.dart'; -import 'package:eatvio_app/features/diets/presentation/widgets/more_options.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/svg.dart'; class DietPreviewScreen extends StatefulWidget { @@ -190,30 +190,12 @@ class _DietPreviewScreenState extends State { style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), actions: [ - PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - disableActions: true, - path: 'diet/${widget.diet.id}', - ), - anchor: const Aligned( - shiftToWithinBound: AxisFlag(x: true, y: true), - follower: Alignment.topRight, - target: Alignment.bottomRight, - ), - child: IconButton( - onPressed: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - icon: SvgPicture.asset( - 'assets/icons/more_button.svg', - width: 20, - height: 20, - ), - ), + CustomPopup( + isAuthor: widget.diet.author.authorIsMe, + path: 'diet/${widget.diet.id}', + complainedContentType: ComplainedContent.diet, ), + const SizedBox(width: 16), ], ), body: SingleChildScrollView( diff --git a/lib/features/diets/presentation/pages/about_diet_screen.dart b/lib/features/diets/presentation/pages/about_diet_screen.dart index d4a07038..8a21395a 100644 --- a/lib/features/diets/presentation/pages/about_diet_screen.dart +++ b/lib/features/diets/presentation/pages/about_diet_screen.dart @@ -19,7 +19,6 @@ import 'package:eatvio_app/features/diets/presentation/widgets/cancel_diet_botto import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart'; import 'package:eatvio_app/core/widgets/main_background_bouble.dart'; -import 'package:eatvio_app/features/diets/presentation/widgets/more_options.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/start_diet_bottom_sheet.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart'; @@ -32,7 +31,6 @@ import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/svg.dart'; class AboutDietScreen extends StatefulWidget { @@ -498,31 +496,13 @@ class _AboutDietScreenState extends State { style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), actions: [ - PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - path: 'diet/${widget.diet.id}', - complainedContentType: ComplainedContent.diet, - id: widget.diet.id, - ), - anchor: const Aligned( - shiftToWithinBound: AxisFlag(x: true, y: true), - follower: Alignment.topRight, - target: Alignment.bottomRight, - ), - child: IconButton( - onPressed: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - icon: SvgPicture.asset( - 'assets/icons/more_button.svg', - width: 20, - height: 20, - ), - ), + CustomPopup( + isAuthor: widget.diet.author.authorIsMe, + path: 'diet/${widget.diet.id}', + complainedContentType: ComplainedContent.diet, + id: widget.diet.id, ), + const SizedBox(width: 16), ], ), body: DataErrorWidget( @@ -542,31 +522,13 @@ class _AboutDietScreenState extends State { style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), actions: [ - PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - path: 'diet/${widget.diet.id}', - complainedContentType: ComplainedContent.diet, - id: widget.diet.id, - ), - anchor: const Aligned( - shiftToWithinBound: AxisFlag(x: true, y: true), - follower: Alignment.topRight, - target: Alignment.bottomRight, - ), - child: IconButton( - onPressed: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - icon: SvgPicture.asset( - 'assets/icons/more_button.svg', - width: 20, - height: 20, - ), - ), + CustomPopup( + isAuthor: widget.diet.author.authorIsMe, + path: 'diet/${widget.diet.id}', + complainedContentType: ComplainedContent.diet, + id: widget.diet.id, ), + const SizedBox(width: 16), ], ), body: const Loader(), diff --git a/lib/features/diets/repositories/diet_repository.dart b/lib/features/diets/repositories/diet_repository.dart index cefc0c26..a3bbbbb3 100644 --- a/lib/features/diets/repositories/diet_repository.dart +++ b/lib/features/diets/repositories/diet_repository.dart @@ -65,6 +65,14 @@ class DietRepository { } } + Future deleteDiet(int id) async { + try { + return await dietsService.deleteDiet(id: id); + } catch (e) { + rethrow; + } + } + void emitLikesStateToFeed(LikesState state) => _dietLikeStateStreamController.add(state); void emitFavoriteStateToFeed(FavoriteState state) => _dietFavoriteStateStreamController.add(state); void emitCommentsStateToFeed(CommentsState state) => _dietCommentsStateStreamController.add(state); diff --git a/lib/features/diets/services/diet_service.dart b/lib/features/diets/services/diet_service.dart index c93e3296..59c2c977 100644 --- a/lib/features/diets/services/diet_service.dart +++ b/lib/features/diets/services/diet_service.dart @@ -39,4 +39,9 @@ abstract class DietService { Future cancelDiet({ @Path() required int id, }); + + @DELETE('/api/diets/delete/{id}') + Future deleteDiet({ + @Path() required int id, + }); } diff --git a/lib/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart b/lib/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart index 23c7ae37..3f1ea0b0 100644 --- a/lib/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart +++ b/lib/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart @@ -29,6 +29,7 @@ class DietsCubit extends Cubit { late final StreamSubscription likesStateSubscription; late final StreamSubscription favoriteStateSubscription; late final StreamSubscription commentsStateSubscription; + DietsCubit({ required this.feedRepository, required this.dietsRepository, @@ -342,4 +343,13 @@ class DietsCubit extends Cubit { }, ); } + + Future deleteDiet(int dietId) async { + try { + await dietsRepository.deleteDiet(dietId); + return true; + } catch (e) { + return false; + } + } } diff --git a/lib/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart b/lib/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart index 18c7ebb8..866d4abb 100644 --- a/lib/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart +++ b/lib/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart @@ -49,7 +49,7 @@ class RecipesCubit extends Cubit { tags: _selectedTags.toList(), query: query, ); - + final recipesTags = (await recipeRepository.getRecipesTags()); emit( state.copyWith( @@ -129,7 +129,7 @@ class RecipesCubit extends Cubit { EasyDebounce.debounce( 'add_or_remove_plan_from_favorite', const Duration(milliseconds: 200), - () async { + () async { try { await aboutRecipeRepository.addToFavorite(slug); } catch (e) { @@ -169,7 +169,7 @@ class RecipesCubit extends Cubit { EasyDebounce.debounce( 'add_or_remove_plan_from_favorite', const Duration(milliseconds: 200), - () async { + () async { try { await aboutRecipeRepository.removeFromFavorite(slug); } catch (e) { @@ -225,7 +225,7 @@ class RecipesCubit extends Cubit { EasyDebounce.debounce( 'like_plan', const Duration(milliseconds: 200), - () async { + () async { try { await aboutRecipeRepository.likeRecipe(slug); } catch (e) { @@ -300,4 +300,13 @@ class RecipesCubit extends Cubit { ), ); } + + Future deleteRecipe(String slug) async { + try { + await aboutRecipeRepository.deleteRecipe(slug); + return true; + } catch (e) { + return false; + } + } } diff --git a/lib/features/my_plans/presentation/cubit/my_plans_cubit.dart b/lib/features/my_plans/presentation/cubit/my_plans_cubit.dart index 6c14003f..33462563 100644 --- a/lib/features/my_plans/presentation/cubit/my_plans_cubit.dart +++ b/lib/features/my_plans/presentation/cubit/my_plans_cubit.dart @@ -204,4 +204,13 @@ class MyPlansCubit extends Cubit { return false; } } + + Future unpublish(String slug) async { + try { + await myPlansRepository.unpublish(slug); + return true; + } catch (e) { + return false; + } + } } diff --git a/lib/features/my_plans/repositories/my_diets_repository.dart b/lib/features/my_plans/repositories/my_diets_repository.dart index a28c88cd..1ee9afb1 100644 --- a/lib/features/my_plans/repositories/my_diets_repository.dart +++ b/lib/features/my_plans/repositories/my_diets_repository.dart @@ -35,5 +35,8 @@ class MyPlansRepository { rethrow; } } - + + Future unpublish(String slug) async { + return await myPlansService.unpublish(slug: slug); + } } diff --git a/lib/features/my_plans/services/my_plans_service.dart b/lib/features/my_plans/services/my_plans_service.dart index ee81e55e..9c5cbf0c 100644 --- a/lib/features/my_plans/services/my_plans_service.dart +++ b/lib/features/my_plans/services/my_plans_service.dart @@ -6,8 +6,7 @@ part 'my_plans_service.g.dart'; @RestApi() abstract class MyPlansService { - - factory MyPlansService(Dio dio, {String baseUrl}) = _MyPlansService; + factory MyPlansService(Dio dio, {String baseUrl}) = _MyPlansService; @GET('/api/plans/index') Future getPlans({ @@ -24,5 +23,8 @@ abstract class MyPlansService { @Path() required String slug, }); - -} \ No newline at end of file + @POST('/api/plans/draft/{slug}') + Future unpublish({ + @Path() required String slug, + }); +} diff --git a/lib/features/plans/presentation/pages/about_plan_screen.dart b/lib/features/plans/presentation/pages/about_plan_screen.dart index 3d35fc51..72cb71bd 100644 --- a/lib/features/plans/presentation/pages/about_plan_screen.dart +++ b/lib/features/plans/presentation/pages/about_plan_screen.dart @@ -15,7 +15,6 @@ import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart' import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart'; import 'package:eatvio_app/core/widgets/main_background_bouble.dart'; -import 'package:eatvio_app/features/diets/presentation/widgets/more_options.dart'; import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_state.dart'; @@ -30,7 +29,6 @@ import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/svg.dart'; import 'package:go_router/go_router.dart'; @@ -143,31 +141,13 @@ class _AboutPlanScreenState extends State { style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), actions: [ - PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - complainedContentType: ComplainedContent.plan, - path: 'plan/${widget.plan.id}', - slug: widget.plan.slug, - ), - anchor: const Aligned( - shiftToWithinBound: AxisFlag(x: true, y: true), - follower: Alignment.topRight, - target: Alignment.bottomRight, - ), - child: IconButton( - onPressed: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - icon: SvgPicture.asset( - 'assets/icons/more_button.svg', - width: 20, - height: 20, - ), - ), + CustomPopup( + isAuthor: widget.plan.author.authorIsMe, + path: 'plan/${widget.plan.id}', + complainedContentType: ComplainedContent.plan, + slug: widget.plan.slug, ), + const SizedBox(width: 16), ], ), body: DataErrorWidget(onReload: () => context.read().getPlan(widget.plan.slug)), @@ -380,31 +360,13 @@ class _AboutPlanScreenState extends State { style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), actions: [ - PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - complainedContentType: ComplainedContent.plan, - path: 'plan/${widget.plan.id}', - slug: widget.plan.slug, - ), - anchor: const Aligned( - shiftToWithinBound: AxisFlag(x: true, y: true), - follower: Alignment.topRight, - target: Alignment.bottomRight, - ), - child: IconButton( - onPressed: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - icon: SvgPicture.asset( - 'assets/icons/more_button.svg', - width: 20, - height: 20, - ), - ), + CustomPopup( + isAuthor: widget.plan.author.authorIsMe, + path: 'plan/${widget.plan.id}', + complainedContentType: ComplainedContent.plan, + slug: widget.plan.slug, ), + const SizedBox(width: 16), ], ), body: SingleChildScrollView( diff --git a/lib/features/recipe/presentation/about_recipe_screen.dart b/lib/features/recipe/presentation/about_recipe_screen.dart index cc65a0c2..2436ba0b 100644 --- a/lib/features/recipe/presentation/about_recipe_screen.dart +++ b/lib/features/recipe/presentation/about_recipe_screen.dart @@ -16,7 +16,6 @@ import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart' import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart'; import 'package:eatvio_app/core/widgets/main_background_bouble.dart'; -import 'package:eatvio_app/features/diets/presentation/widgets/more_options.dart'; import 'package:eatvio_app/features/feed/models/recipes_model.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_state.dart'; @@ -32,7 +31,6 @@ import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/svg.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -398,23 +396,13 @@ class _AboutRecipeScreenState extends State { style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), actions: [ - PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - complainedContentType: ComplainedContent.recipe, - path: 'recipe/${widget.recipe.slug}', - slug: widget.recipe.slug, - ), - anchor: const Aligned(shiftToWithinBound: AxisFlag(x: true, y: true), follower: Alignment.topRight, target: Alignment.bottomRight), - child: IconButton( - onPressed: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - icon: SvgPicture.asset('assets/icons/more_button.svg', width: 20, height: 20), - ), + CustomPopup( + isAuthor: widget.recipe.author.authorIsMe, + path: 'recipe/${widget.recipe.slug}', + complainedContentType: ComplainedContent.recipe, + slug: widget.recipe.slug, ), + const SizedBox(width: 16), ], ), body: DataErrorWidget(onReload: () => context.read().getRecipe(widget.recipe.slug, index: widget.index)), @@ -432,23 +420,13 @@ class _AboutRecipeScreenState extends State { style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), ), actions: [ - PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions( - complainedContentType: ComplainedContent.recipe, - path: 'recipe/${widget.recipe.slug}', - slug: widget.recipe.slug, - ), - anchor: const Aligned(shiftToWithinBound: AxisFlag(x: true, y: true), follower: Alignment.topRight, target: Alignment.bottomRight), - child: IconButton( - onPressed: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); - }, - icon: SvgPicture.asset('assets/icons/more_button.svg', width: 20, height: 20), - ), + CustomPopup( + isAuthor: widget.recipe.author.authorIsMe, + path: 'recipe/${widget.recipe.slug}', + complainedContentType: ComplainedContent.recipe, + slug: widget.recipe.slug, ), + const SizedBox(width: 16), ], ), body: const Loader(), diff --git a/lib/features/recipe/repositories/about_recipe_repository.dart b/lib/features/recipe/repositories/about_recipe_repository.dart index a9178a65..df433f5e 100644 --- a/lib/features/recipe/repositories/about_recipe_repository.dart +++ b/lib/features/recipe/repositories/about_recipe_repository.dart @@ -47,6 +47,14 @@ class AboutRecipeRepository { } } + Future deleteRecipe(String slug) async { + try { + await recipeService.deleteRecipe(slug: slug); + } catch (e) { + rethrow; + } + } + void emitLikesStateToFeed(LikesState state) => _recipeLikeStateStreamController.add(state); void emitFavoriteStateToFeed(FavoriteState state) => _recipeFavoriteStateStreamController.add(state); void emitCommentsStateToFeed(CommentsState state) => _recipeCommentsStateStreamController.add(state); diff --git a/lib/features/recipe/services/about_recipe_service.dart b/lib/features/recipe/services/about_recipe_service.dart index b326f970..21170ed4 100644 --- a/lib/features/recipe/services/about_recipe_service.dart +++ b/lib/features/recipe/services/about_recipe_service.dart @@ -25,4 +25,9 @@ abstract class AboutRecipeService { @GET('/api/recipes/show/{id}') Future getRecipe(@Path() String id); + + @DELETE('/api/recipes/delete/{slug}') + Future deleteRecipe({ + @Path() required String slug, + }); } From c0f66b7a93fc349febe681f6ed42fe028c7359c3 Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Mon, 12 May 2025 09:00:58 +0000 Subject: [PATCH 26/26] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20.gitea/workflows/test-flight-cd.yaml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/test-flight-cd.yaml | 50 ++-------------------------- 1 file changed, 3 insertions(+), 47 deletions(-) diff --git a/.gitea/workflows/test-flight-cd.yaml b/.gitea/workflows/test-flight-cd.yaml index 1bb78c94..70fbcd94 100644 --- a/.gitea/workflows/test-flight-cd.yaml +++ b/.gitea/workflows/test-flight-cd.yaml @@ -8,51 +8,7 @@ jobs: build: runs-on: [macos-arm64] steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Remove cache - run: rm -rf /Users/gitea_runner_ci/Library/Developer/Xcode/DerivedData/* - - - name: Download provisioning - uses: mso7nueh/apple-provisioning-profiles-downloader@v1.0.8 - with: - profile-type: IOS_APP_STORE - bundle-id: com.eatvioapp.app - issuer-id: ${{ vars.APPSTORE_ISSUER_ID }} - api-key-id: ${{ vars.APPSTORE_KEY_ID }} - api-private-key: ${{ vars.APPSTORE_PRIVATE_KEY }} - - - name: Unlock keychain + - name: Run Fastlane run: | - security unlock-keychain -p "EeChahboo9Ei" login.keychain - security set-key-partition-list -S apple-tool:,apple: -s -k "EeChahboo9Ei" login.keychain - - - name: Ensure private key access - run: | - security set-key-partition-list -S apple-tool:,apple: -s -k "EeChahboo9Ei" ~/Library/Keychains/login.keychain-db - - - name: List identities in keychain - run: | - security list-keychains - security find-identity -v -p codesigning login.keychain - - - name: Set keychain default - run: | - security default-keychain -s login.keychain - - - name: Build with Flutter - run: | - flutter clean - flutter pub get - flutter pub upgrade - flutter pub run build_runner build --delete-conflicting-outputs - flutter build ipa --export-options-plist=Support/ExportOptions.plist --release - - - name: Upload to TestFlight - uses: mso7nueh/upload-testflight-build@master - with: - app-path: ./build/ios/ipa/eatvio_app.ipa - issuer-id: ${{ vars.APPSTORE_ISSUER_ID }} - api-key-id: ${{ vars.APPSTORE_KEY_ID }} - api-private-key: ${{ vars.APPSTORE_PRIVATE_KEY }} + cd ios + fastlane beta \ No newline at end of file