forked from eatvio/eatvio_app
feat(diary): add finish day button and glossary moderation request
This commit is contained in:
@@ -34,6 +34,19 @@ class DiaryCubit extends Cubit<DiaryState> {
|
||||
}
|
||||
}
|
||||
|
||||
finishDiaryDay({
|
||||
required String date,
|
||||
required int diaryId,
|
||||
}) async {
|
||||
await diaryRepository.finishDiaryDay(
|
||||
diaryId: diaryId,
|
||||
wakeUp: '',
|
||||
retire: '',
|
||||
comment: '',
|
||||
);
|
||||
await getDiaryDay(date, false);
|
||||
}
|
||||
|
||||
Future<NutritionDataModel> getNutrition(int index) async {
|
||||
return (await diaryRepository.getNutritionData(index));
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:eatvio_app/features/diary/models/diary_day_model.dart';
|
||||
import 'package:eatvio_app/features/diary/models/nutrition_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/finish_day_button_widget.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/widgets/glossary_moderation_request_modal_widget.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/widgets/satiety_rating_widget.dart';
|
||||
import 'package:eatvio_app/features/glossary/models/glossary_model.dart';
|
||||
@@ -33,6 +34,7 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
CalendarFormat _calendarFormat = CalendarFormat.month;
|
||||
DateTime _focusedDay = DateTime.now();
|
||||
DateTime? _selectedDay = DateTime.now();
|
||||
late final DateTime? today;
|
||||
int currentSelectedMealNumber = 1;
|
||||
GlossaryModel? selectedGlossary;
|
||||
final productNameController = TextEditingController();
|
||||
@@ -40,6 +42,13 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
int currentSelectedPhotoIndex = 0;
|
||||
String time = "${DateTime.now().hour.toString().padLeft(2, '0')}:${DateTime.now().minute.toString().padLeft(2, '0')}";
|
||||
DateTime _time = DateTime.now();
|
||||
bool enableQuantity = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
today = _selectedDay;
|
||||
}
|
||||
|
||||
_showDatePicker() {
|
||||
showModalBottomSheet<void>(
|
||||
@@ -65,12 +74,6 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
useSafeArea: false,
|
||||
context: context,
|
||||
builder: (context) => GlossaryModerationRequestModalWidget(),
|
||||
);
|
||||
},
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Ink(
|
||||
@@ -128,6 +131,11 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
state.currentSelectedSatietyRating,
|
||||
),
|
||||
if (state is DiaryStateLoaded) _chart(state.nutrition),
|
||||
if (state is DiaryStateLoaded && state.diary.closed == 0 && _selectedDay == today)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12),
|
||||
child: FinishDayButtonWidget(diaryDay: state.diary),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
],
|
||||
),
|
||||
@@ -504,17 +512,27 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
_chartData('Калории',
|
||||
'${nutrition.dailyNutritionIntake.energy.toStringAsFixed(0)} из ${nutrition.dailyNutritionRecommendation.energy.toStringAsFixed(0)} ккал'),
|
||||
_chartData(
|
||||
'Калории',
|
||||
'${nutrition.dailyNutritionIntake.energy.toStringAsFixed(0)} из ${nutrition.dailyNutritionRecommendation.energy.toStringAsFixed(0)} ккал',
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_chartData('Белки',
|
||||
'${nutrition.dailyNutritionIntake.proteins.toStringAsFixed(0)} из ${nutrition.dailyNutritionRecommendation.proteins.toStringAsFixed(0)} грамм'),
|
||||
_chartData(
|
||||
'Белки',
|
||||
'${nutrition.dailyNutritionIntake.proteins.toStringAsFixed(0)} из ${nutrition.dailyNutritionRecommendation.proteins.toStringAsFixed(
|
||||
0,
|
||||
)} грамм',
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_chartData('Жиры',
|
||||
'${nutrition.dailyNutritionIntake.fats.toStringAsFixed(0)} из ${nutrition.dailyNutritionRecommendation.fats.toStringAsFixed(0)} грамм'),
|
||||
_chartData(
|
||||
'Жиры',
|
||||
'${nutrition.dailyNutritionIntake.fats.toStringAsFixed(0)} из ${nutrition.dailyNutritionRecommendation.fats.toStringAsFixed(0)} грамм',
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_chartData('Углеводы',
|
||||
'${nutrition.dailyNutritionIntake.carbohydrates.toStringAsFixed(0)} из ${nutrition.dailyNutritionRecommendation.carbohydrates.toStringAsFixed(0)} грамм'),
|
||||
_chartData(
|
||||
'Углеводы',
|
||||
'${nutrition.dailyNutritionIntake.carbohydrates.toStringAsFixed(0)} из ${nutrition.dailyNutritionRecommendation.carbohydrates.toStringAsFixed(0)} грамм',
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -835,7 +853,10 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
return StatefulBuilder(
|
||||
builder: (BuildContext context, void Function(void Function()) setState) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16) + EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||
padding: const EdgeInsets.all(16) +
|
||||
EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).viewInsets.bottom,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -878,6 +899,11 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
selectedGlossary = suggestionsList.first;
|
||||
});
|
||||
}
|
||||
if (suggestionsList.length == 0) {
|
||||
setState(() {
|
||||
enableQuantity = true;
|
||||
});
|
||||
}
|
||||
return suggestionsList;
|
||||
},
|
||||
builder: (context, controller, focusNode) {
|
||||
@@ -888,21 +914,31 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
hintText: 'Введите название продукта/блюда',
|
||||
);
|
||||
},
|
||||
emptyBuilder: (context) => const SizedBox.shrink(),
|
||||
emptyBuilder: (context) {
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
errorBuilder: (context, object) => Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text('Нет подходящих вариантов'),
|
||||
),
|
||||
),
|
||||
if (selectedGlossary != null)
|
||||
if (selectedGlossary != null || enableQuantity)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: CustomTextFormField(
|
||||
controller: quantityController,
|
||||
keyboardType: TextInputType.number,
|
||||
hintText: selectedGlossary!.grInPortion == 0 ? '100' : '1',
|
||||
hintText: selectedGlossary == null
|
||||
? '100'
|
||||
: selectedGlossary!.grInPortion == 0
|
||||
? '100'
|
||||
: '1',
|
||||
autofocus: true,
|
||||
suffixText: selectedGlossary!.grInPortion == 0 ? 'гр' : 'шт',
|
||||
suffixText: selectedGlossary == null
|
||||
? 'гр'
|
||||
: selectedGlossary?.grInPortion == 0
|
||||
? 'гр'
|
||||
: 'шт',
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
@@ -941,7 +977,11 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
diaryId: diaryDay.id,
|
||||
mealId: diaryDay.meals[currentSelectedMealNumber - 1].id,
|
||||
productName: productNameController.text,
|
||||
portion: selectedGlossary!.grInPortion == 0 ? 'gr' : 'pcs',
|
||||
portion: selectedGlossary == null
|
||||
? 'gr'
|
||||
: selectedGlossary!.grInPortion == 0
|
||||
? 'gr'
|
||||
: 'pcs',
|
||||
quantity: int.parse(quantityController.text),
|
||||
date: _selectedDay.toString(),
|
||||
);
|
||||
@@ -951,12 +991,27 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
mealId: diaryDay.meals[currentSelectedMealNumber - 1].id,
|
||||
portionId: portionId,
|
||||
productName: productNameController.text,
|
||||
portion: selectedGlossary!.grInPortion == 0 ? 'gr' : 'pcs',
|
||||
portion: selectedGlossary == null
|
||||
? 'gr'
|
||||
: selectedGlossary!.grInPortion == 0
|
||||
? 'gr'
|
||||
: 'pcs',
|
||||
quantity: int.parse(quantityController.text),
|
||||
date: _selectedDay.toString(),
|
||||
);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
if (enableQuantity) {
|
||||
showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
useSafeArea: false,
|
||||
context: context,
|
||||
builder: (context) => GlossaryModerationRequestModalWidget(),
|
||||
);
|
||||
}
|
||||
setState(() {
|
||||
enableQuantity = false;
|
||||
});
|
||||
},
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Ink(
|
||||
@@ -994,6 +1049,7 @@ class _DiaryScreenState extends State<DiaryScreen> {
|
||||
onTap: () {
|
||||
setState(() {
|
||||
selectedGlossary = null;
|
||||
enableQuantity = false;
|
||||
productNameController.clear();
|
||||
quantityController.clear();
|
||||
});
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
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/theme/theme_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class FinishDayButtonWidget extends StatefulWidget {
|
||||
final DiaryDayModel diaryDay;
|
||||
|
||||
const FinishDayButtonWidget({
|
||||
super.key,
|
||||
required this.diaryDay,
|
||||
});
|
||||
|
||||
@override
|
||||
State<FinishDayButtonWidget> createState() => _FinishDayButtonWidgetState();
|
||||
}
|
||||
|
||||
class _FinishDayButtonWidgetState extends State<FinishDayButtonWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: () => context.read<DiaryCubit>().finishDiaryDay(
|
||||
diaryId: widget.diaryDay.id,
|
||||
date: widget.diaryDay.date,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Ink(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||
width: MediaQuery.of(context).size.width - 16,
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.blueA400,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(16),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'Закончить день',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'package:eatvio_app/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/widgets/custom_text_form_field.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class GlossaryModerationRequestModalWidget extends StatefulWidget {
|
||||
@@ -7,11 +9,201 @@ class GlossaryModerationRequestModalWidget extends StatefulWidget {
|
||||
State<GlossaryModerationRequestModalWidget> createState() => _GlossaryModerationRequestModalWidgetState();
|
||||
}
|
||||
|
||||
enum GlossaryState { alert, addNewProduct, finish }
|
||||
|
||||
class _GlossaryModerationRequestModalWidgetState extends State<GlossaryModerationRequestModalWidget> {
|
||||
GlossaryState glossaryState = GlossaryState.alert;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
child: Column(),
|
||||
if (glossaryState == GlossaryState.alert) {
|
||||
return _alert();
|
||||
} else {
|
||||
return _addNewGlossaryProduct();
|
||||
}
|
||||
}
|
||||
|
||||
_alert() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'Добавление нового продукта',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Color(0xFF323F4B),
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Оу... а у нас нет в базе калорийности такого продукта. Если вы знаете, давайте её запишем?',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Color(0xFF323F4B),
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Ink(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFF6F6F6),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(16),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'В другой раз',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF6D6F71),
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
glossaryState = GlossaryState.addNewProduct;
|
||||
});
|
||||
},
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Ink(
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.blueA400,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(16),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'Записать',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_addNewGlossaryProduct() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'Добавление нового продукта',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Color(0xFF323F4B),
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Если у вас данные по нему\nпросим заполнить значения на 100гр.',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Color(0xFF323F4B),
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
CustomTextFormField(
|
||||
hintText: 'Сырный суп',
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomTextFormField(
|
||||
hintText: 'Калории',
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: CustomTextFormField(
|
||||
hintText: 'Белки',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomTextFormField(
|
||||
hintText: 'Жиры',
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: CustomTextFormField(
|
||||
hintText: 'Углеводы',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
InkWell(
|
||||
onTap: () {},
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Ink(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||
width: MediaQuery.of(context).size.width - 16,
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.blueA400,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(16),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'Сохранить',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user