feat(diary): diet

This commit is contained in:
Alex
2024-12-24 16:52:06 +03:00
parent c6a60eccd9
commit 67d8bb58ee
13 changed files with 451 additions and 86 deletions

View File

@@ -0,0 +1,3 @@
<svg width="20" height="18" viewBox="0 0 20 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.42466 0.301085C3.34908 0.328863 3.20265 0.407568 3.10817 0.472385C2.95701 0.57424 0.429854 3.61601 0.113368 4.07899C-0.0377893 4.29659 -0.0377893 4.57437 0.113368 4.79197C0.297591 5.05587 2.68777 7.9819 2.90033 8.1995C3.15069 8.45876 3.40104 8.58377 3.72697 8.61155C4.24185 8.65784 4.64809 8.4495 4.88899 8.01893C5.00236 7.81522 5.02598 7.71337 5.02598 7.46799C5.02598 7.10687 4.97402 7.00038 4.41663 6.20406C4.19461 5.8846 4.01512 5.61145 4.01512 5.59756C4.01512 5.57904 6.13604 5.57441 8.72933 5.57904L13.4388 5.59293L13.9017 5.71793C15.3377 6.10683 16.495 7.00964 17.1422 8.25505C17.5815 9.09767 17.7468 9.83381 17.6996 10.7737C17.5909 13.0839 15.8479 15.0099 13.5144 15.4081C13.2215 15.459 12.7256 15.4775 11.6013 15.4775C9.82995 15.4775 9.70713 15.496 9.37648 15.8247C9.01748 16.172 8.93718 16.7044 9.16864 17.1489C9.282 17.3665 9.60794 17.6396 9.8205 17.6998C10.1039 17.7785 13.1649 17.76 13.7222 17.6767C15.3708 17.4313 16.7879 16.7322 17.9263 15.5933C18.9136 14.6117 19.5701 13.3987 19.8961 11.9728C19.9858 11.5839 20 11.3802 20 10.5236C20 9.66714 19.9858 9.46343 19.8961 9.06989C19.4473 7.09761 18.3042 5.47718 16.599 4.40307C15.9613 4.00028 14.9504 3.60675 14.0907 3.43082C13.5805 3.32433 13.5664 3.32433 8.80019 3.31044C6.16911 3.30581 4.01512 3.28729 4.01512 3.27341C4.01512 3.26415 4.20406 2.9771 4.4308 2.63912C4.66226 2.30578 4.88427 1.96318 4.92206 1.8891C5.30468 1.14833 4.71422 0.236267 3.85451 0.250156C3.69863 0.250156 3.50496 0.273306 3.42466 0.301085Z" fill="#2F80ED"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -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<Portion> portions,
@JsonKey(name: "portions") required List<PortionModel> portions,
@JsonKey(name: "photos") required List<DiaryPhotoModel> 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<String, dynamic> json) => _$PortionFromJson(json);
factory PortionModel.fromJson(Map<String, dynamic> json) => _$PortionModelFromJson(json);
}
@freezed

View File

@@ -545,7 +545,7 @@ mixin _$MealModel {
@JsonKey(name: "mood")
int get mood => throw _privateConstructorUsedError;
@JsonKey(name: "portions")
List<Portion> get portions => throw _privateConstructorUsedError;
List<PortionModel> get portions => throw _privateConstructorUsedError;
@JsonKey(name: "photos")
List<DiaryPhotoModel> 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<Portion> portions,
@JsonKey(name: "portions") List<PortionModel> portions,
@JsonKey(name: "photos") List<DiaryPhotoModel> 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<Portion>,
as List<PortionModel>,
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<Portion> portions,
@JsonKey(name: "portions") List<PortionModel> portions,
@JsonKey(name: "photos") List<DiaryPhotoModel> photos});
}
@@ -693,7 +693,7 @@ class __$$MealModelImplCopyWithImpl<$Res>
portions: null == portions
? _value._portions
: portions // ignore: cast_nullable_to_non_nullable
as List<Portion>,
as List<PortionModel>,
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<Portion> portions,
@JsonKey(name: "portions") required final List<PortionModel> portions,
@JsonKey(name: "photos") required final List<DiaryPhotoModel> photos})
: _portions = portions,
_photos = photos;
@@ -734,10 +734,10 @@ class _$MealModelImpl implements _MealModel {
@override
@JsonKey(name: "mood")
final int mood;
final List<Portion> _portions;
final List<PortionModel> _portions;
@override
@JsonKey(name: "portions")
List<Portion> get portions {
List<PortionModel> 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<Portion> portions,
@JsonKey(name: "portions") required final List<PortionModel> portions,
@JsonKey(name: "photos")
required final List<DiaryPhotoModel> photos}) = _$MealModelImpl;
@@ -831,7 +831,7 @@ abstract class _MealModel implements MealModel {
int get mood;
@override
@JsonKey(name: "portions")
List<Portion> get portions;
List<PortionModel> get portions;
@override
@JsonKey(name: "photos")
List<DiaryPhotoModel> get photos;
@@ -1071,14 +1071,16 @@ abstract class _DiaryPhotoModel implements DiaryPhotoModel {
throw _privateConstructorUsedError;
}
Portion _$PortionFromJson(Map<String, dynamic> json) {
return _Portion.fromJson(json);
PortionModel _$PortionModelFromJson(Map<String, dynamic> 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<String, dynamic> 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<Portion> get copyWith => throw _privateConstructorUsedError;
$PortionModelCopyWith<PortionModel> 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<String, dynamic> json) =>
_$$PortionImplFromJson(json);
factory _$PortionModelImpl.fromJson(Map<String, dynamic> 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<String, dynamic> 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<String, dynamic> json) = _$PortionImpl.fromJson;
factory _PortionModel.fromJson(Map<String, dynamic> 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;
}

View File

@@ -53,7 +53,7 @@ _$MealModelImpl _$$MealModelImplFromJson(Map<String, dynamic> json) =>
satiety: (json['satiety'] as num).toInt(),
mood: (json['mood'] as num).toInt(),
portions: (json['portions'] as List<dynamic>)
.map((e) => Portion.fromJson(e as Map<String, dynamic>))
.map((e) => PortionModel.fromJson(e as Map<String, dynamic>))
.toList(),
photos: (json['photos'] as List<dynamic>)
.map((e) => DiaryPhotoModel.fromJson(e as Map<String, dynamic>))
@@ -89,24 +89,40 @@ Map<String, dynamic> _$$DiaryPhotoModelImplToJson(
'size': instance.size,
};
_$PortionImpl _$$PortionImplFromJson(Map<String, dynamic> json) =>
_$PortionImpl(
_$PortionModelImpl _$$PortionModelImplFromJson(Map<String, dynamic> 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<String, dynamic> _$$PortionImplToJson(_$PortionImpl instance) =>
Map<String, dynamic> _$$PortionModelImplToJson(_$PortionModelImpl instance) =>
<String, dynamic>{
'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<String, dynamic> json) =>
_$TypeOfDiaryModelImpl(

View File

@@ -102,15 +102,73 @@ class DiaryCubit extends Cubit<DiaryState> {
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();
}

View File

@@ -35,7 +35,6 @@ class _DiaryScreenState extends State<DiaryScreen> {
CalendarWidget(),
if (state is DiaryStateLoading) const Loader(),
if (state is DiaryStateLoaded) MealWidget(),
const SizedBox(height: 12),
CaloriesChartWidget(),
],
),

View File

@@ -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<AddProductModal> {
: 'pcs',
quantity: int.parse(quantityController.text),
productNutritionalValueId: selectedGlossary?.id,
portionState: widget.portion!.portionState,
);
}
Navigator.of(context).pop();

View File

@@ -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<ProductWidget> {
late final DiaryCubit diaryCubit;
late String portionName;
late bool isEaten;
@override
void initState() {
super.initState();
diaryCubit = context.read<DiaryCubit>();
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<ProductWidget> {
return BlocBuilder<DiaryCubit, DiaryState>(
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();
},

View File

@@ -51,6 +51,7 @@ class DiaryRepository {
required String portion,
required int quantity,
int? productNutritionalValueId,
required PortionState portionState,
}) async {
Map<String, Object?> 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<void> markEaten({
required int diaryId,
required int mealId,
required int portionId,
}) async {
return await diaryService.markEaten(
diary: diaryId,
typeNo: mealId,
portionModel: portionId,
);
}
Future<void> unmarkEaten({
required int diaryId,
required int mealId,
required int portionId,
}) async {
return await diaryService.unmarkEaten(
diary: diaryId,
typeNo: mealId,
portionModel: portionId,
);
}
Future<void> restore({
required int diaryId,
required int mealId,
required int portionId,
}) async {
return await diaryService.restore(
diary: diaryId,
typeNo: mealId,
portionModel: portionId,
);
}
Future<void> deleteProductFromMeal({
required int diaryId,
required int mealId,

View File

@@ -60,6 +60,27 @@ abstract class DiaryService {
@Body() required Map<String, dynamic> productInMeal,
});
@POST('/api/diary/{diary}/meals/{type_no}/{portionModel}/mark_eaten')
Future<void> 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<void> 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<void> 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<void> deleteProductFromMeal({
@Path('diary') required int diary,

View File

@@ -271,6 +271,93 @@ class _DiaryService implements DiaryService {
await _dio.fetch<void>(_options);
}
@override
Future<void> markEaten({
required int diary,
required int typeNo,
required int portionModel,
}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options = _setStreamType<void>(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<void>(_options);
}
@override
Future<void> unmarkEaten({
required int diary,
required int typeNo,
required int portionModel,
}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options = _setStreamType<void>(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<void>(_options);
}
@override
Future<void> restore({
required int diary,
required int typeNo,
required int portionModel,
}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options = _setStreamType<void>(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<void>(_options);
}
@override
Future<void> deleteProductFromMeal({
required int diary,

View File

@@ -907,10 +907,12 @@ class _ProfileScreenState extends State<ProfileScreen> {
value: 'view',
child: Text('Посмотреть'),
),
if (widget.author == null)
PopupMenuItem(
value: 'replace',
child: Text('Заменить'),
),
if (widget.author == null)
PopupMenuItem(
value: 'delete',
child: Text('Удалить'),

View File

@@ -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