forked from eatvio/eatvio_app
feat: diary add update
This commit is contained in:
@@ -78,6 +78,7 @@ class DiaryCubit extends Cubit<DiaryState> {
|
||||
required String productName,
|
||||
required String portion,
|
||||
required int quantity,
|
||||
int? productNutritionalValueId,
|
||||
}) async {
|
||||
await diaryRepository.addPortionToMeal(
|
||||
diaryId: diaryId,
|
||||
@@ -85,6 +86,7 @@ class DiaryCubit extends Cubit<DiaryState> {
|
||||
productName: productName,
|
||||
portion: portion,
|
||||
quantity: quantity,
|
||||
productNutritionalValueId: productNutritionalValueId,
|
||||
);
|
||||
await fetchDiary();
|
||||
}
|
||||
@@ -96,6 +98,7 @@ class DiaryCubit extends Cubit<DiaryState> {
|
||||
required String productName,
|
||||
required String portion,
|
||||
required int quantity,
|
||||
int? productNutritionalValueId,
|
||||
}) async {
|
||||
await diaryRepository.updateProductInMeal(
|
||||
diaryId: diaryId,
|
||||
@@ -104,6 +107,7 @@ class DiaryCubit extends Cubit<DiaryState> {
|
||||
productName: productName,
|
||||
portion: portion,
|
||||
quantity: quantity,
|
||||
productNutritionalValueId: productNutritionalValueId,
|
||||
);
|
||||
await fetchDiary();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:easy_debounce/easy_debounce.dart';
|
||||
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';
|
||||
@@ -7,7 +8,6 @@ import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/widgets/glossary_moderation_request_modal_widget.dart';
|
||||
import 'package:eatvio_app/features/glossary/models/glossary_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
||||
|
||||
class AddProductModal extends StatefulWidget {
|
||||
final Portion? portion;
|
||||
@@ -29,6 +29,7 @@ class _AddProductModalState extends State<AddProductModal> {
|
||||
final productNameController = TextEditingController();
|
||||
final quantityController = TextEditingController();
|
||||
GlossaryModel? selectedGlossary;
|
||||
List<GlossaryModel> glossariesList = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -36,6 +37,11 @@ class _AddProductModalState extends State<AddProductModal> {
|
||||
if (widget.portion != null) {
|
||||
productNameController.text = widget.portion!.productName;
|
||||
quantityController.text = '${widget.portion!.quantity}';
|
||||
selectedGlossary = GlossaryModel(
|
||||
id: widget.portion!.id,
|
||||
name: widget.portion!.productName,
|
||||
grInPortion: widget.portion!.portion == 'gr' ? 0 : 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,47 +72,20 @@ class _AddProductModalState extends State<AddProductModal> {
|
||||
children: [
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.8 - 32,
|
||||
child: TypeAheadField(
|
||||
child: CustomTextFormField(
|
||||
controller: productNameController,
|
||||
itemBuilder: (context, glossary) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
glossary.name,
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
autofocus: true,
|
||||
hintText: 'Введите название продукта/блюда',
|
||||
onChanged: (query) {
|
||||
EasyDebounce.debounce(
|
||||
'client-typing',
|
||||
Duration(milliseconds: 250),
|
||||
() async {
|
||||
glossariesList = await widget.diaryCubit.getGlossaryListByQuery(query);
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
},
|
||||
itemSeparatorBuilder: (context, index) => CustomDivider(),
|
||||
onSelected: (newSelectedGlossary) async {
|
||||
setState(() {
|
||||
selectedGlossary = newSelectedGlossary;
|
||||
productNameController.text = selectedGlossary!.name;
|
||||
});
|
||||
},
|
||||
suggestionsCallback: (query) async {
|
||||
if (query.length < 1) return [];
|
||||
final suggestionsList = await widget.diaryCubit.getGlossaryListByQuery(query);
|
||||
return suggestionsList;
|
||||
},
|
||||
builder: (context, controller, focusNode) {
|
||||
return CustomTextFormField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
focusNode: focusNode,
|
||||
hintText: 'Введите название продукта/блюда',
|
||||
);
|
||||
},
|
||||
emptyBuilder: (context) {
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
errorBuilder: (context, object) => Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text('Нет подходящих вариантов'),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
@@ -129,7 +108,55 @@ class _AddProductModalState extends State<AddProductModal> {
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
glossariesList.isEmpty ? const SizedBox(height: 16) : const SizedBox.shrink(),
|
||||
Container(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: 256,
|
||||
),
|
||||
child: ListView.separated(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, i) {
|
||||
return ListTile(
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
selectedGlossary = glossariesList[i];
|
||||
productNameController.text = selectedGlossary!.name;
|
||||
});
|
||||
glossariesList = await widget.diaryCubit.getGlossaryListByQuery(glossariesList[i].name);
|
||||
setState(() {});
|
||||
},
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
title: Text(
|
||||
glossariesList[i].name,
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
subtitle: glossariesList[i].grInPortion == 0
|
||||
? null
|
||||
: Text(
|
||||
'${glossariesList[i].grInPortion} г',
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
),
|
||||
trailing: Icon(
|
||||
Icons.keyboard_arrow_right_rounded,
|
||||
color: Colors.grey.shade400,
|
||||
),
|
||||
contentPadding: EdgeInsets.only(left: 12),
|
||||
);
|
||||
},
|
||||
itemCount: glossariesList.length,
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return CustomDivider();
|
||||
},
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
if (widget.portion != null)
|
||||
@@ -170,6 +197,7 @@ class _AddProductModalState extends State<AddProductModal> {
|
||||
? 'gr'
|
||||
: 'pcs',
|
||||
quantity: int.parse(quantityController.text),
|
||||
productNutritionalValueId: selectedGlossary?.id,
|
||||
);
|
||||
} else {
|
||||
widget.diaryCubit.updateProductInMeal(
|
||||
@@ -183,6 +211,7 @@ class _AddProductModalState extends State<AddProductModal> {
|
||||
? 'gr'
|
||||
: 'pcs',
|
||||
quantity: int.parse(quantityController.text),
|
||||
productNutritionalValueId: selectedGlossary?.id,
|
||||
);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
|
||||
@@ -19,11 +19,13 @@ class ProductWidget extends StatefulWidget {
|
||||
|
||||
class _ProductWidgetState extends State<ProductWidget> {
|
||||
late final DiaryCubit diaryCubit;
|
||||
late String portionName;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
diaryCubit = context.read<DiaryCubit>();
|
||||
portionName = widget.portion == 'gr' ? 'гр' : 'шт';
|
||||
}
|
||||
|
||||
updateProductInMeal(DiaryStateLoaded state, Portion portion) {
|
||||
@@ -51,7 +53,7 @@ class _ProductWidgetState extends State<ProductWidget> {
|
||||
onTap: () => updateProductInMeal(state, widget.portion),
|
||||
trailing: Icon(Icons.more_vert_rounded),
|
||||
title: Text(widget.portion.productName),
|
||||
subtitle: Text('${widget.portion.quantity} ${widget.portion == 'gr' ? 'гр' : 'шт'}'),
|
||||
subtitle: Text('${widget.portion.quantity} ${widget.portion.portion == 'gr' ? 'гр' : 'шт'}'),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
|
||||
@@ -30,6 +30,7 @@ class DiaryRepository {
|
||||
required String productName,
|
||||
required String portion,
|
||||
required int quantity,
|
||||
int? productNutritionalValueId,
|
||||
}) async {
|
||||
return await diaryService.addProductToMeal(
|
||||
diary: diaryId,
|
||||
@@ -37,6 +38,7 @@ class DiaryRepository {
|
||||
productName: productName,
|
||||
portion: portion,
|
||||
quantity: quantity,
|
||||
productNutritionalValueId: productNutritionalValueId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -47,16 +49,19 @@ class DiaryRepository {
|
||||
required String productName,
|
||||
required String portion,
|
||||
required int quantity,
|
||||
int? productNutritionalValueId,
|
||||
}) async {
|
||||
Map<String, Object?> data = ProductInMeal(
|
||||
productName: productName,
|
||||
portion: portion,
|
||||
quantity: quantity,
|
||||
).toJson();
|
||||
data['product_nutritional_value_id'] = productNutritionalValueId;
|
||||
return await diaryService.updateProductInMeal(
|
||||
diary: diaryId,
|
||||
typeNo: mealId,
|
||||
portionModel: portionId,
|
||||
productInMeal: ProductInMeal(
|
||||
productName: productName,
|
||||
portion: portion,
|
||||
quantity: quantity,
|
||||
),
|
||||
productInMeal: data,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ abstract class DiaryService {
|
||||
@Part(name: 'product_name') required String productName,
|
||||
@Part(name: 'portion') required String portion,
|
||||
@Part(name: 'quantity') required int quantity,
|
||||
@Part(name: 'product_nutritional_value_id') int? productNutritionalValueId,
|
||||
});
|
||||
|
||||
@PUT('/api/diary/{diary}/meals/{type_no}/portions/{portionModel}/update')
|
||||
@@ -55,7 +56,7 @@ abstract class DiaryService {
|
||||
@Path('diary') required int diary,
|
||||
@Path('type_no') required int typeNo,
|
||||
@Path('portionModel') required int portionModel,
|
||||
@Body() required ProductInMeal productInMeal,
|
||||
@Body() required Map<String, dynamic> productInMeal,
|
||||
});
|
||||
|
||||
@DELETE('/api/diary/{diary}/meals/{type_no}/portions/{portionModel}/delete')
|
||||
|
||||
@@ -196,9 +196,11 @@ class _DiaryService implements DiaryService {
|
||||
required String productName,
|
||||
required String portion,
|
||||
required int quantity,
|
||||
int? productNutritionalValueId,
|
||||
}) async {
|
||||
final _extra = <String, dynamic>{};
|
||||
final queryParameters = <String, dynamic>{};
|
||||
queryParameters.removeWhere((k, v) => v == null);
|
||||
final _headers = <String, dynamic>{};
|
||||
final _data = FormData();
|
||||
_data.fields.add(MapEntry(
|
||||
@@ -213,6 +215,12 @@ class _DiaryService implements DiaryService {
|
||||
'quantity',
|
||||
quantity.toString(),
|
||||
));
|
||||
if (productNutritionalValueId != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'product_nutritional_value_id',
|
||||
productNutritionalValueId.toString(),
|
||||
));
|
||||
}
|
||||
final _options = _setStreamType<void>(Options(
|
||||
method: 'POST',
|
||||
headers: _headers,
|
||||
@@ -237,12 +245,13 @@ class _DiaryService implements DiaryService {
|
||||
required int diary,
|
||||
required int typeNo,
|
||||
required int portionModel,
|
||||
required ProductInMeal productInMeal,
|
||||
required Map<String, dynamic> productInMeal,
|
||||
}) async {
|
||||
final _extra = <String, dynamic>{};
|
||||
final queryParameters = <String, dynamic>{};
|
||||
final _headers = <String, dynamic>{};
|
||||
final _data = productInMeal;
|
||||
final _data = <String, dynamic>{};
|
||||
_data.addAll(productInMeal);
|
||||
final _options = _setStreamType<void>(Options(
|
||||
method: 'PUT',
|
||||
headers: _headers,
|
||||
|
||||
Reference in New Issue
Block a user