feat(diary): add finish day button and glossary moderation request

This commit is contained in:
mso7nueh
2024-11-13 14:09:15 +03:00
parent 02f82aaa42
commit 0eaa493d96
14 changed files with 649 additions and 49 deletions

View File

@@ -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));
}

View File

@@ -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();
});

View File

@@ -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,
),
),
),
),
);
}
}

View File

@@ -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,
),
),
),
),
),
],
),
);
}
}