diff --git a/assets/icons/revert_product.svg b/assets/icons/revert_product.svg
new file mode 100644
index 00000000..fa80931d
--- /dev/null
+++ b/assets/icons/revert_product.svg
@@ -0,0 +1,3 @@
+
diff --git a/lib/features/diary/models/diary_day_model.dart b/lib/features/diary/models/diary_day_model.dart
index a9a6cfff..e9e129d9 100644
--- a/lib/features/diary/models/diary_day_model.dart
+++ b/lib/features/diary/models/diary_day_model.dart
@@ -4,6 +4,17 @@ part 'diary_day_model.freezed.dart';
part 'diary_day_model.g.dart';
+enum PortionState {
+ active,
+ filled_diet,
+ eaten_from_diet,
+ eaten,
+ eaten_deleted,
+ eaten_edited,
+ commented,
+ reviewed_by_user,
+}
+
@freezed
class DiaryDayWrapperModel with _$DiaryDayWrapperModel {
const factory DiaryDayWrapperModel({
@@ -37,7 +48,7 @@ class MealModel with _$MealModel {
@JsonKey(name: "type_no") required int typeNumber,
@JsonKey(name: "satiety") required int satiety,
@JsonKey(name: "mood") required int mood,
- @JsonKey(name: "portions") required List portions,
+ @JsonKey(name: "portions") required List portions,
@JsonKey(name: "photos") required List photos,
}) = _MealModel;
@@ -57,16 +68,18 @@ class DiaryPhotoModel with _$DiaryPhotoModel {
}
@freezed
-class Portion with _$Portion {
- const factory Portion({
+class PortionModel with _$PortionModel {
+ const factory PortionModel({
@JsonKey(name: "id") required int id,
+ @JsonKey(name: "product_nutritional_value_id") int? productNutritionalValueId,
@JsonKey(name: "product_name") required String productName,
@JsonKey(name: "quantity") required int quantity,
@JsonKey(name: "portion") required String portion,
@JsonKey(name: "from_diet") required bool fromDiet,
- }) = _Portion;
+ @JsonKey(name: "state") required PortionState portionState,
+ }) = _PortionModel;
- factory Portion.fromJson(Map json) => _$PortionFromJson(json);
+ factory PortionModel.fromJson(Map json) => _$PortionModelFromJson(json);
}
@freezed
diff --git a/lib/features/diary/models/diary_day_model.freezed.dart b/lib/features/diary/models/diary_day_model.freezed.dart
index 2e87e426..38983ff8 100644
--- a/lib/features/diary/models/diary_day_model.freezed.dart
+++ b/lib/features/diary/models/diary_day_model.freezed.dart
@@ -545,7 +545,7 @@ mixin _$MealModel {
@JsonKey(name: "mood")
int get mood => throw _privateConstructorUsedError;
@JsonKey(name: "portions")
- List get portions => throw _privateConstructorUsedError;
+ List get portions => throw _privateConstructorUsedError;
@JsonKey(name: "photos")
List get photos => throw _privateConstructorUsedError;
@@ -570,7 +570,7 @@ abstract class $MealModelCopyWith<$Res> {
@JsonKey(name: "type_no") int typeNumber,
@JsonKey(name: "satiety") int satiety,
@JsonKey(name: "mood") int mood,
- @JsonKey(name: "portions") List portions,
+ @JsonKey(name: "portions") List portions,
@JsonKey(name: "photos") List photos});
}
@@ -621,7 +621,7 @@ class _$MealModelCopyWithImpl<$Res, $Val extends MealModel>
portions: null == portions
? _value.portions
: portions // ignore: cast_nullable_to_non_nullable
- as List,
+ as List,
photos: null == photos
? _value.photos
: photos // ignore: cast_nullable_to_non_nullable
@@ -644,7 +644,7 @@ abstract class _$$MealModelImplCopyWith<$Res>
@JsonKey(name: "type_no") int typeNumber,
@JsonKey(name: "satiety") int satiety,
@JsonKey(name: "mood") int mood,
- @JsonKey(name: "portions") List portions,
+ @JsonKey(name: "portions") List portions,
@JsonKey(name: "photos") List photos});
}
@@ -693,7 +693,7 @@ class __$$MealModelImplCopyWithImpl<$Res>
portions: null == portions
? _value._portions
: portions // ignore: cast_nullable_to_non_nullable
- as List,
+ as List,
photos: null == photos
? _value._photos
: photos // ignore: cast_nullable_to_non_nullable
@@ -711,7 +711,7 @@ class _$MealModelImpl implements _MealModel {
@JsonKey(name: "type_no") required this.typeNumber,
@JsonKey(name: "satiety") required this.satiety,
@JsonKey(name: "mood") required this.mood,
- @JsonKey(name: "portions") required final List portions,
+ @JsonKey(name: "portions") required final List portions,
@JsonKey(name: "photos") required final List photos})
: _portions = portions,
_photos = photos;
@@ -734,10 +734,10 @@ class _$MealModelImpl implements _MealModel {
@override
@JsonKey(name: "mood")
final int mood;
- final List _portions;
+ final List _portions;
@override
@JsonKey(name: "portions")
- List get portions {
+ List get portions {
if (_portions is EqualUnmodifiableListView) return _portions;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_portions);
@@ -807,7 +807,7 @@ abstract class _MealModel implements MealModel {
@JsonKey(name: "type_no") required final int typeNumber,
@JsonKey(name: "satiety") required final int satiety,
@JsonKey(name: "mood") required final int mood,
- @JsonKey(name: "portions") required final List portions,
+ @JsonKey(name: "portions") required final List portions,
@JsonKey(name: "photos")
required final List photos}) = _$MealModelImpl;
@@ -831,7 +831,7 @@ abstract class _MealModel implements MealModel {
int get mood;
@override
@JsonKey(name: "portions")
- List get portions;
+ List get portions;
@override
@JsonKey(name: "photos")
List get photos;
@@ -1071,14 +1071,16 @@ abstract class _DiaryPhotoModel implements DiaryPhotoModel {
throw _privateConstructorUsedError;
}
-Portion _$PortionFromJson(Map json) {
- return _Portion.fromJson(json);
+PortionModel _$PortionModelFromJson(Map json) {
+ return _PortionModel.fromJson(json);
}
/// @nodoc
-mixin _$Portion {
+mixin _$PortionModel {
@JsonKey(name: "id")
int get id => throw _privateConstructorUsedError;
+ @JsonKey(name: "product_nutritional_value_id")
+ int? get productNutritionalValueId => throw _privateConstructorUsedError;
@JsonKey(name: "product_name")
String get productName => throw _privateConstructorUsedError;
@JsonKey(name: "quantity")
@@ -1087,55 +1089,68 @@ mixin _$Portion {
String get portion => throw _privateConstructorUsedError;
@JsonKey(name: "from_diet")
bool get fromDiet => throw _privateConstructorUsedError;
+ @JsonKey(name: "state")
+ PortionState get portionState => throw _privateConstructorUsedError;
- /// Serializes this Portion to a JSON map.
+ /// Serializes this PortionModel to a JSON map.
Map toJson() => throw _privateConstructorUsedError;
- /// Create a copy of Portion
+ /// Create a copy of PortionModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
- $PortionCopyWith get copyWith => throw _privateConstructorUsedError;
+ $PortionModelCopyWith get copyWith =>
+ throw _privateConstructorUsedError;
}
/// @nodoc
-abstract class $PortionCopyWith<$Res> {
- factory $PortionCopyWith(Portion value, $Res Function(Portion) then) =
- _$PortionCopyWithImpl<$Res, Portion>;
+abstract class $PortionModelCopyWith<$Res> {
+ factory $PortionModelCopyWith(
+ PortionModel value, $Res Function(PortionModel) then) =
+ _$PortionModelCopyWithImpl<$Res, PortionModel>;
@useResult
$Res call(
{@JsonKey(name: "id") int id,
+ @JsonKey(name: "product_nutritional_value_id")
+ int? productNutritionalValueId,
@JsonKey(name: "product_name") String productName,
@JsonKey(name: "quantity") int quantity,
@JsonKey(name: "portion") String portion,
- @JsonKey(name: "from_diet") bool fromDiet});
+ @JsonKey(name: "from_diet") bool fromDiet,
+ @JsonKey(name: "state") PortionState portionState});
}
/// @nodoc
-class _$PortionCopyWithImpl<$Res, $Val extends Portion>
- implements $PortionCopyWith<$Res> {
- _$PortionCopyWithImpl(this._value, this._then);
+class _$PortionModelCopyWithImpl<$Res, $Val extends PortionModel>
+ implements $PortionModelCopyWith<$Res> {
+ _$PortionModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
- /// Create a copy of Portion
+ /// Create a copy of PortionModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
+ Object? productNutritionalValueId = freezed,
Object? productName = null,
Object? quantity = null,
Object? portion = null,
Object? fromDiet = null,
+ Object? portionState = null,
}) {
return _then(_value.copyWith(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
+ productNutritionalValueId: freezed == productNutritionalValueId
+ ? _value.productNutritionalValueId
+ : productNutritionalValueId // ignore: cast_nullable_to_non_nullable
+ as int?,
productName: null == productName
? _value.productName
: productName // ignore: cast_nullable_to_non_nullable
@@ -1152,49 +1167,63 @@ class _$PortionCopyWithImpl<$Res, $Val extends Portion>
? _value.fromDiet
: fromDiet // ignore: cast_nullable_to_non_nullable
as bool,
+ portionState: null == portionState
+ ? _value.portionState
+ : portionState // ignore: cast_nullable_to_non_nullable
+ as PortionState,
) as $Val);
}
}
/// @nodoc
-abstract class _$$PortionImplCopyWith<$Res> implements $PortionCopyWith<$Res> {
- factory _$$PortionImplCopyWith(
- _$PortionImpl value, $Res Function(_$PortionImpl) then) =
- __$$PortionImplCopyWithImpl<$Res>;
+abstract class _$$PortionModelImplCopyWith<$Res>
+ implements $PortionModelCopyWith<$Res> {
+ factory _$$PortionModelImplCopyWith(
+ _$PortionModelImpl value, $Res Function(_$PortionModelImpl) then) =
+ __$$PortionModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{@JsonKey(name: "id") int id,
+ @JsonKey(name: "product_nutritional_value_id")
+ int? productNutritionalValueId,
@JsonKey(name: "product_name") String productName,
@JsonKey(name: "quantity") int quantity,
@JsonKey(name: "portion") String portion,
- @JsonKey(name: "from_diet") bool fromDiet});
+ @JsonKey(name: "from_diet") bool fromDiet,
+ @JsonKey(name: "state") PortionState portionState});
}
/// @nodoc
-class __$$PortionImplCopyWithImpl<$Res>
- extends _$PortionCopyWithImpl<$Res, _$PortionImpl>
- implements _$$PortionImplCopyWith<$Res> {
- __$$PortionImplCopyWithImpl(
- _$PortionImpl _value, $Res Function(_$PortionImpl) _then)
+class __$$PortionModelImplCopyWithImpl<$Res>
+ extends _$PortionModelCopyWithImpl<$Res, _$PortionModelImpl>
+ implements _$$PortionModelImplCopyWith<$Res> {
+ __$$PortionModelImplCopyWithImpl(
+ _$PortionModelImpl _value, $Res Function(_$PortionModelImpl) _then)
: super(_value, _then);
- /// Create a copy of Portion
+ /// Create a copy of PortionModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
+ Object? productNutritionalValueId = freezed,
Object? productName = null,
Object? quantity = null,
Object? portion = null,
Object? fromDiet = null,
+ Object? portionState = null,
}) {
- return _then(_$PortionImpl(
+ return _then(_$PortionModelImpl(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
+ productNutritionalValueId: freezed == productNutritionalValueId
+ ? _value.productNutritionalValueId
+ : productNutritionalValueId // ignore: cast_nullable_to_non_nullable
+ as int?,
productName: null == productName
? _value.productName
: productName // ignore: cast_nullable_to_non_nullable
@@ -1211,27 +1240,37 @@ class __$$PortionImplCopyWithImpl<$Res>
? _value.fromDiet
: fromDiet // ignore: cast_nullable_to_non_nullable
as bool,
+ portionState: null == portionState
+ ? _value.portionState
+ : portionState // ignore: cast_nullable_to_non_nullable
+ as PortionState,
));
}
}
/// @nodoc
@JsonSerializable()
-class _$PortionImpl implements _Portion {
- const _$PortionImpl(
+class _$PortionModelImpl implements _PortionModel {
+ const _$PortionModelImpl(
{@JsonKey(name: "id") required this.id,
+ @JsonKey(name: "product_nutritional_value_id")
+ this.productNutritionalValueId,
@JsonKey(name: "product_name") required this.productName,
@JsonKey(name: "quantity") required this.quantity,
@JsonKey(name: "portion") required this.portion,
- @JsonKey(name: "from_diet") required this.fromDiet});
+ @JsonKey(name: "from_diet") required this.fromDiet,
+ @JsonKey(name: "state") required this.portionState});
- factory _$PortionImpl.fromJson(Map json) =>
- _$$PortionImplFromJson(json);
+ factory _$PortionModelImpl.fromJson(Map json) =>
+ _$$PortionModelImplFromJson(json);
@override
@JsonKey(name: "id")
final int id;
@override
+ @JsonKey(name: "product_nutritional_value_id")
+ final int? productNutritionalValueId;
+ @override
@JsonKey(name: "product_name")
final String productName;
@override
@@ -1243,63 +1282,78 @@ class _$PortionImpl implements _Portion {
@override
@JsonKey(name: "from_diet")
final bool fromDiet;
+ @override
+ @JsonKey(name: "state")
+ final PortionState portionState;
@override
String toString() {
- return 'Portion(id: $id, productName: $productName, quantity: $quantity, portion: $portion, fromDiet: $fromDiet)';
+ return 'PortionModel(id: $id, productNutritionalValueId: $productNutritionalValueId, productName: $productName, quantity: $quantity, portion: $portion, fromDiet: $fromDiet, portionState: $portionState)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
- other is _$PortionImpl &&
+ other is _$PortionModelImpl &&
(identical(other.id, id) || other.id == id) &&
+ (identical(other.productNutritionalValueId,
+ productNutritionalValueId) ||
+ other.productNutritionalValueId == productNutritionalValueId) &&
(identical(other.productName, productName) ||
other.productName == productName) &&
(identical(other.quantity, quantity) ||
other.quantity == quantity) &&
(identical(other.portion, portion) || other.portion == portion) &&
(identical(other.fromDiet, fromDiet) ||
- other.fromDiet == fromDiet));
+ other.fromDiet == fromDiet) &&
+ (identical(other.portionState, portionState) ||
+ other.portionState == portionState));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
- int get hashCode =>
- Object.hash(runtimeType, id, productName, quantity, portion, fromDiet);
+ int get hashCode => Object.hash(runtimeType, id, productNutritionalValueId,
+ productName, quantity, portion, fromDiet, portionState);
- /// Create a copy of Portion
+ /// Create a copy of PortionModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
- _$$PortionImplCopyWith<_$PortionImpl> get copyWith =>
- __$$PortionImplCopyWithImpl<_$PortionImpl>(this, _$identity);
+ _$$PortionModelImplCopyWith<_$PortionModelImpl> get copyWith =>
+ __$$PortionModelImplCopyWithImpl<_$PortionModelImpl>(this, _$identity);
@override
Map toJson() {
- return _$$PortionImplToJson(
+ return _$$PortionModelImplToJson(
this,
);
}
}
-abstract class _Portion implements Portion {
- const factory _Portion(
+abstract class _PortionModel implements PortionModel {
+ const factory _PortionModel(
{@JsonKey(name: "id") required final int id,
+ @JsonKey(name: "product_nutritional_value_id")
+ final int? productNutritionalValueId,
@JsonKey(name: "product_name") required final String productName,
@JsonKey(name: "quantity") required final int quantity,
@JsonKey(name: "portion") required final String portion,
- @JsonKey(name: "from_diet") required final bool fromDiet}) =
- _$PortionImpl;
+ @JsonKey(name: "from_diet") required final bool fromDiet,
+ @JsonKey(name: "state") required final PortionState portionState}) =
+ _$PortionModelImpl;
- factory _Portion.fromJson(Map json) = _$PortionImpl.fromJson;
+ factory _PortionModel.fromJson(Map json) =
+ _$PortionModelImpl.fromJson;
@override
@JsonKey(name: "id")
int get id;
@override
+ @JsonKey(name: "product_nutritional_value_id")
+ int? get productNutritionalValueId;
+ @override
@JsonKey(name: "product_name")
String get productName;
@override
@@ -1311,12 +1365,15 @@ abstract class _Portion implements Portion {
@override
@JsonKey(name: "from_diet")
bool get fromDiet;
+ @override
+ @JsonKey(name: "state")
+ PortionState get portionState;
- /// Create a copy of Portion
+ /// Create a copy of PortionModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
- _$$PortionImplCopyWith<_$PortionImpl> get copyWith =>
+ _$$PortionModelImplCopyWith<_$PortionModelImpl> get copyWith =>
throw _privateConstructorUsedError;
}
diff --git a/lib/features/diary/models/diary_day_model.g.dart b/lib/features/diary/models/diary_day_model.g.dart
index b055fcfc..9e4d7520 100644
--- a/lib/features/diary/models/diary_day_model.g.dart
+++ b/lib/features/diary/models/diary_day_model.g.dart
@@ -53,7 +53,7 @@ _$MealModelImpl _$$MealModelImplFromJson(Map json) =>
satiety: (json['satiety'] as num).toInt(),
mood: (json['mood'] as num).toInt(),
portions: (json['portions'] as List)
- .map((e) => Portion.fromJson(e as Map))
+ .map((e) => PortionModel.fromJson(e as Map))
.toList(),
photos: (json['photos'] as List)
.map((e) => DiaryPhotoModel.fromJson(e as Map))
@@ -89,24 +89,40 @@ Map _$$DiaryPhotoModelImplToJson(
'size': instance.size,
};
-_$PortionImpl _$$PortionImplFromJson(Map json) =>
- _$PortionImpl(
+_$PortionModelImpl _$$PortionModelImplFromJson(Map json) =>
+ _$PortionModelImpl(
id: (json['id'] as num).toInt(),
+ productNutritionalValueId:
+ (json['product_nutritional_value_id'] as num?)?.toInt(),
productName: json['product_name'] as String,
quantity: (json['quantity'] as num).toInt(),
portion: json['portion'] as String,
fromDiet: json['from_diet'] as bool,
+ portionState: $enumDecode(_$PortionStateEnumMap, json['state']),
);
-Map _$$PortionImplToJson(_$PortionImpl instance) =>
+Map _$$PortionModelImplToJson(_$PortionModelImpl instance) =>
{
'id': instance.id,
+ 'product_nutritional_value_id': instance.productNutritionalValueId,
'product_name': instance.productName,
'quantity': instance.quantity,
'portion': instance.portion,
'from_diet': instance.fromDiet,
+ 'state': _$PortionStateEnumMap[instance.portionState]!,
};
+const _$PortionStateEnumMap = {
+ PortionState.active: 'active',
+ PortionState.filled_diet: 'filled_diet',
+ PortionState.eaten_from_diet: 'eaten_from_diet',
+ PortionState.eaten: 'eaten',
+ PortionState.eaten_deleted: 'eaten_deleted',
+ PortionState.eaten_edited: 'eaten_edited',
+ PortionState.commented: 'commented',
+ PortionState.reviewed_by_user: 'reviewed_by_user',
+};
+
_$TypeOfDiaryModelImpl _$$TypeOfDiaryModelImplFromJson(
Map json) =>
_$TypeOfDiaryModelImpl(
diff --git a/lib/features/diary/presentation/bloc/diary_cubit.dart b/lib/features/diary/presentation/bloc/diary_cubit.dart
index 4f5c7f31..542240e2 100644
--- a/lib/features/diary/presentation/bloc/diary_cubit.dart
+++ b/lib/features/diary/presentation/bloc/diary_cubit.dart
@@ -102,15 +102,73 @@ class DiaryCubit extends Cubit {
required String portion,
required int quantity,
int? productNutritionalValueId,
+ required PortionState portionState,
}) async {
- await diaryRepository.updateProductInMeal(
+ if (portionState == PortionState.filled_diet || portionState == PortionState.eaten_from_diet) {
+ // await diaryRepository.deleteProductFromMeal(
+ // diaryId: diaryId,
+ // mealId: mealId,
+ // portionId: portionId,
+ // );
+ // todo delete product from diet
+ await diaryRepository.addPortionToMeal(
+ diaryId: diaryId,
+ mealId: mealId,
+ productName: productName,
+ portion: portion,
+ quantity: quantity,
+ productNutritionalValueId: productNutritionalValueId,
+ );
+ } else {
+ await diaryRepository.updateProductInMeal(
+ diaryId: diaryId,
+ mealId: mealId,
+ portionId: portionId,
+ productName: productName,
+ portion: portion,
+ quantity: quantity,
+ productNutritionalValueId: productNutritionalValueId,
+ portionState: portionState,
+ );
+ }
+ await fetchDiary();
+ }
+
+ markEaten({
+ required int diaryId,
+ required int mealId,
+ required int portionId,
+ }) async {
+ await diaryRepository.markEaten(
+ diaryId: diaryId,
+ mealId: mealId,
+ portionId: portionId,
+ );
+ await fetchDiary();
+ }
+
+ unmarkEaten({
+ required int diaryId,
+ required int mealId,
+ required int portionId,
+ }) async {
+ await diaryRepository.unmarkEaten(
+ diaryId: diaryId,
+ mealId: mealId,
+ portionId: portionId,
+ );
+ await fetchDiary();
+ }
+
+ restore({
+ required int diaryId,
+ required int mealId,
+ required int portionId,
+ }) async {
+ await diaryRepository.restore(
diaryId: diaryId,
mealId: mealId,
portionId: portionId,
- productName: productName,
- portion: portion,
- quantity: quantity,
- productNutritionalValueId: productNutritionalValueId,
);
await fetchDiary();
}
diff --git a/lib/features/diary/presentation/pages/diary_screen.dart b/lib/features/diary/presentation/pages/diary_screen.dart
index 24f916ac..c6c8e291 100644
--- a/lib/features/diary/presentation/pages/diary_screen.dart
+++ b/lib/features/diary/presentation/pages/diary_screen.dart
@@ -35,7 +35,6 @@ class _DiaryScreenState extends State {
CalendarWidget(),
if (state is DiaryStateLoading) const Loader(),
if (state is DiaryStateLoaded) MealWidget(),
- const SizedBox(height: 12),
CaloriesChartWidget(),
],
),
diff --git a/lib/features/diary/presentation/widgets/add_product_modal.dart b/lib/features/diary/presentation/widgets/add_product_modal.dart
index 563beeb4..01c5a64d 100644
--- a/lib/features/diary/presentation/widgets/add_product_modal.dart
+++ b/lib/features/diary/presentation/widgets/add_product_modal.dart
@@ -10,7 +10,7 @@ import 'package:eatvio_app/features/glossary/models/glossary_model.dart';
import 'package:flutter/material.dart';
class AddProductModal extends StatefulWidget {
- final Portion? portion;
+ final PortionModel? portion;
final DiaryStateLoaded state;
final DiaryCubit diaryCubit;
@@ -221,6 +221,7 @@ class _AddProductModalState extends State {
: 'pcs',
quantity: int.parse(quantityController.text),
productNutritionalValueId: selectedGlossary?.id,
+ portionState: widget.portion!.portionState,
);
}
Navigator.of(context).pop();
diff --git a/lib/features/diary/presentation/widgets/product_widget.dart b/lib/features/diary/presentation/widgets/product_widget.dart
index 05e0c2b6..3c6e01df 100644
--- a/lib/features/diary/presentation/widgets/product_widget.dart
+++ b/lib/features/diary/presentation/widgets/product_widget.dart
@@ -1,12 +1,15 @@
+import 'package:dotted_border/dotted_border.dart';
+import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/diary/models/diary_day_model.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/add_product_modal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:flutter_svg/flutter_svg.dart';
class ProductWidget extends StatefulWidget {
- final Portion portion;
+ final PortionModel portion;
const ProductWidget({
super.key,
@@ -20,15 +23,17 @@ class ProductWidget extends StatefulWidget {
class _ProductWidgetState extends State {
late final DiaryCubit diaryCubit;
late String portionName;
+ late bool isEaten;
@override
void initState() {
super.initState();
diaryCubit = context.read();
portionName = widget.portion == 'gr' ? 'гр' : 'шт';
+ isEaten = widget.portion.portionState == PortionState.eaten_from_diet ? true : false;
}
- updateProductInMeal(DiaryStateLoaded state, Portion portion) {
+ updateProductInMeal(DiaryStateLoaded state, PortionModel portion) {
return showModalBottomSheet(
useRootNavigator: true,
context: context,
@@ -49,16 +54,80 @@ class _ProductWidgetState extends State {
return BlocBuilder(
builder: (context, state) {
if (state is DiaryStateLoaded) {
- return ListTile(
- onTap: () => updateProductInMeal(state, widget.portion),
- trailing: Icon(Icons.more_vert_rounded),
- title: Text(widget.portion.productName),
- subtitle: Text('${widget.portion.quantity} ${widget.portion.portion == 'gr' ? 'гр' : 'шт'}'),
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(20),
- ),
- tileColor: Color(0xFFF6FAFF),
- );
+ if (widget.portion.portionState == PortionState.eaten_deleted) {
+ return DottedBorder(
+ dashPattern: [6, 6],
+ padding: EdgeInsets.zero,
+ borderPadding: EdgeInsets.zero,
+ borderType: BorderType.RRect,
+ radius: Radius.circular(20),
+ color: Color(0xFF6D6F71),
+ child: ListTile(
+ onTap: () => diaryCubit.restore(
+ diaryId: state.diary.id,
+ mealId: state.selectedMeal,
+ portionId: widget.portion.id,
+ ),
+ trailing: SvgPicture.asset(
+ 'assets/icons/revert_product.svg',
+ ),
+ horizontalTitleGap: 0,
+ contentPadding: widget.portion.fromDiet ? EdgeInsets.only(right: 24, left: 4) : null,
+ title: Text(widget.portion.productName),
+ subtitle: Text('${widget.portion.quantity} ${widget.portion.portion == 'gr' ? 'гр' : 'шт'}'),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(20),
+ ),
+ tileColor: Color(0xFFF6FAFF03).withValues(alpha: 0.01),
+ ),
+ );
+ } else {
+ return ListTile(
+ onTap: () => updateProductInMeal(state, widget.portion),
+ trailing: Icon(Icons.more_vert_rounded),
+ leading: widget.portion.fromDiet
+ ? Transform.scale(
+ 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,
+ );
+ }
+ },
+ activeColor: appTheme.blueA400,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(4),
+ ),
+ side: BorderSide(
+ color: Color(0xFF809CB9),
+ ),
+ ),
+ )
+ : null,
+ horizontalTitleGap: 0,
+ contentPadding: widget.portion.fromDiet ? EdgeInsets.only(right: 24, left: 4) : null,
+ title: Text(widget.portion.productName),
+ subtitle: Text('${widget.portion.quantity} ${widget.portion.portion == 'gr' ? 'гр' : 'шт'}'),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(20),
+ ),
+ tileColor: Color(0xFFF6FAFF),
+ );
+ }
}
return const SizedBox.shrink();
},
diff --git a/lib/features/diary/repositories/diary_repository.dart b/lib/features/diary/repositories/diary_repository.dart
index d5b696a6..b6d41427 100644
--- a/lib/features/diary/repositories/diary_repository.dart
+++ b/lib/features/diary/repositories/diary_repository.dart
@@ -51,6 +51,7 @@ class DiaryRepository {
required String portion,
required int quantity,
int? productNutritionalValueId,
+ required PortionState portionState,
}) async {
Map data = ProductInMeal(
productName: productName,
@@ -58,6 +59,7 @@ class DiaryRepository {
quantity: quantity,
).toJson();
data['product_nutritional_value_id'] = productNutritionalValueId;
+ data['state'] = portionState.name;
return await diaryService.updateProductInMeal(
diary: diaryId,
typeNo: mealId,
@@ -66,6 +68,42 @@ class DiaryRepository {
);
}
+ Future markEaten({
+ required int diaryId,
+ required int mealId,
+ required int portionId,
+ }) async {
+ return await diaryService.markEaten(
+ diary: diaryId,
+ typeNo: mealId,
+ portionModel: portionId,
+ );
+ }
+
+ Future unmarkEaten({
+ required int diaryId,
+ required int mealId,
+ required int portionId,
+ }) async {
+ return await diaryService.unmarkEaten(
+ diary: diaryId,
+ typeNo: mealId,
+ portionModel: portionId,
+ );
+ }
+
+ Future restore({
+ required int diaryId,
+ required int mealId,
+ required int portionId,
+ }) async {
+ return await diaryService.restore(
+ diary: diaryId,
+ typeNo: mealId,
+ portionModel: portionId,
+ );
+ }
+
Future deleteProductFromMeal({
required int diaryId,
required int mealId,
diff --git a/lib/features/diary/services/diary_service.dart b/lib/features/diary/services/diary_service.dart
index bb14e8cf..6a519b71 100644
--- a/lib/features/diary/services/diary_service.dart
+++ b/lib/features/diary/services/diary_service.dart
@@ -60,6 +60,27 @@ abstract class DiaryService {
@Body() required Map productInMeal,
});
+ @POST('/api/diary/{diary}/meals/{type_no}/{portionModel}/mark_eaten')
+ Future markEaten({
+ @Path('diary') required int diary,
+ @Path('type_no') required int typeNo,
+ @Path('portionModel') required int portionModel,
+ });
+
+ @POST('/api/diary/{diary}/meals/{type_no}/{portionModel}/unmark_eaten')
+ Future unmarkEaten({
+ @Path('diary') required int diary,
+ @Path('type_no') required int typeNo,
+ @Path('portionModel') required int portionModel,
+ });
+
+ @POST('/api/diary/{diary}/meals/{type_no}/portions/{portionModel}/restore')
+ Future restore({
+ @Path('diary') required int diary,
+ @Path('type_no') required int typeNo,
+ @Path('portionModel') required int portionModel,
+ });
+
@DELETE('/api/diary/{diary}/meals/{type_no}/portions/{portionModel}/delete')
Future deleteProductFromMeal({
@Path('diary') required int diary,
diff --git a/lib/features/diary/services/diary_service.g.dart b/lib/features/diary/services/diary_service.g.dart
index 13951947..4544f6bf 100644
--- a/lib/features/diary/services/diary_service.g.dart
+++ b/lib/features/diary/services/diary_service.g.dart
@@ -271,6 +271,93 @@ class _DiaryService implements DiaryService {
await _dio.fetch(_options);
}
+ @override
+ Future markEaten({
+ required int diary,
+ required int typeNo,
+ required int portionModel,
+ }) async {
+ final _extra = {};
+ final queryParameters = {};
+ final _headers = {};
+ const Map? _data = null;
+ final _options = _setStreamType(Options(
+ method: 'POST',
+ headers: _headers,
+ extra: _extra,
+ )
+ .compose(
+ _dio.options,
+ '/api/diary/${diary}/meals/${typeNo}/${portionModel}/mark_eaten',
+ queryParameters: queryParameters,
+ data: _data,
+ )
+ .copyWith(
+ baseUrl: _combineBaseUrls(
+ _dio.options.baseUrl,
+ baseUrl,
+ )));
+ await _dio.fetch(_options);
+ }
+
+ @override
+ Future unmarkEaten({
+ required int diary,
+ required int typeNo,
+ required int portionModel,
+ }) async {
+ final _extra = {};
+ final queryParameters = {};
+ final _headers = {};
+ const Map? _data = null;
+ final _options = _setStreamType(Options(
+ method: 'POST',
+ headers: _headers,
+ extra: _extra,
+ )
+ .compose(
+ _dio.options,
+ '/api/diary/${diary}/meals/${typeNo}/${portionModel}/unmark_eaten',
+ queryParameters: queryParameters,
+ data: _data,
+ )
+ .copyWith(
+ baseUrl: _combineBaseUrls(
+ _dio.options.baseUrl,
+ baseUrl,
+ )));
+ await _dio.fetch(_options);
+ }
+
+ @override
+ Future restore({
+ required int diary,
+ required int typeNo,
+ required int portionModel,
+ }) async {
+ final _extra = {};
+ final queryParameters = {};
+ final _headers = {};
+ const Map? _data = null;
+ final _options = _setStreamType(Options(
+ method: 'POST',
+ headers: _headers,
+ extra: _extra,
+ )
+ .compose(
+ _dio.options,
+ '/api/diary/${diary}/meals/${typeNo}/portions/${portionModel}/restore',
+ queryParameters: queryParameters,
+ data: _data,
+ )
+ .copyWith(
+ baseUrl: _combineBaseUrls(
+ _dio.options.baseUrl,
+ baseUrl,
+ )));
+ await _dio.fetch(_options);
+ }
+
@override
Future deleteProductFromMeal({
required int diary,
diff --git a/lib/features/profile/presentation/pages/profile_screen.dart b/lib/features/profile/presentation/pages/profile_screen.dart
index 488322fb..6ee797a3 100644
--- a/lib/features/profile/presentation/pages/profile_screen.dart
+++ b/lib/features/profile/presentation/pages/profile_screen.dart
@@ -907,10 +907,12 @@ class _ProfileScreenState extends State {
value: 'view',
child: Text('Посмотреть'),
),
+ if (widget.author == null)
PopupMenuItem(
value: 'replace',
child: Text('Заменить'),
),
+ if (widget.author == null)
PopupMenuItem(
value: 'delete',
child: Text('Удалить'),
diff --git a/test/features/profile/services/profile_service_test.mocks.dart b/test/features/profile/services/profile_service_test.mocks.dart
index bb0c633a..c853a94e 100644
--- a/test/features/profile/services/profile_service_test.mocks.dart
+++ b/test/features/profile/services/profile_service_test.mocks.dart
@@ -1,4 +1,4 @@
-// Mocks generated by Mockito 5.4.4 from annotations
+// Mocks generated by Mockito 5.4.5 from annotations
// in eatvio_app/test/features/profile/services/profile_service_test.dart.
// Do not manually edit this file.
@@ -25,6 +25,7 @@ import 'package:mockito/mockito.dart' as _i1;
// ignore_for_file: deprecated_member_use_from_same_package
// ignore_for_file: implementation_imports
// ignore_for_file: invalid_use_of_visible_for_testing_member
+// ignore_for_file: must_be_immutable
// ignore_for_file: prefer_const_constructors
// ignore_for_file: unnecessary_parenthesis
// ignore_for_file: camel_case_types