forked from eatvio/eatvio_app
feat(diary): consultant comments
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CommentContainer extends StatelessWidget {
|
||||
const CommentContainer({super.key, required this.comment});
|
||||
final String comment;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.commentBg,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Text(
|
||||
comment,
|
||||
style: TextStyles.textMMediumAuto.copyWith(color: appTheme.black),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,26 @@
|
||||
import 'package:easy_debounce/easy_debounce.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class CommentForConsultantWidget extends StatefulWidget {
|
||||
const CommentForConsultantWidget({super.key});
|
||||
|
||||
const CommentForConsultantWidget({super.key, this.comment});
|
||||
final String? comment;
|
||||
@override
|
||||
State<CommentForConsultantWidget> createState() => _CommentForConsultantWidgetState();
|
||||
}
|
||||
|
||||
class _CommentForConsultantWidgetState extends State<CommentForConsultantWidget> {
|
||||
final controller = TextEditingController();
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.comment != null) controller.text = widget.comment ?? '';
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
@@ -22,19 +31,22 @@ class _CommentForConsultantWidgetState extends State<CommentForConsultantWidget>
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
Text(
|
||||
'Дополнительная информация для консультанта',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Color(0xFF323F4B), height: 1),
|
||||
style: TextStyles.textLSemibold.copyWith(color: appTheme.black),
|
||||
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
CustomTextFormField(
|
||||
controller: controller,
|
||||
onChanged: (value) {
|
||||
EasyDebounce.debounce('client-typing', const Duration(seconds: 3), () => context.read<DiaryCubit>().fillComment(value));
|
||||
EasyDebounce.debounce('client-typing', const Duration(milliseconds: 500), () => context.read<DiaryCubit>().fillComment(value));
|
||||
},
|
||||
hintText: 'Укажите дополнительную информацию',
|
||||
minLines: 4,
|
||||
maxLines: 4,
|
||||
readOnly: widget.comment != null,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import 'package:easy_debounce/easy_debounce.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_cubit.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_state.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class DayCommentWidget extends StatelessWidget {
|
||||
const DayCommentWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = S.of(context);
|
||||
return BlocBuilder<ConsultantCommentsCubit, ConsultantCommentsState>(
|
||||
builder: (context, state) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
s.dayCommentTitle,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyles.textLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
CustomTextFormField(
|
||||
onChanged: (value) {
|
||||
EasyDebounce.debounce('client-typing', const Duration(milliseconds: 500), () => context.read<ConsultantCommentsCubit>().setComment(value));
|
||||
},
|
||||
hasError: state.dayCommentNeedsToBeFilled,
|
||||
hintText: s.typeDayComment,
|
||||
minLines: 4,
|
||||
maxLines: 4,
|
||||
readOnly: false,
|
||||
),
|
||||
if (state.dayCommentNeedsToBeFilled) ...[const SizedBox(height: 4), const ValidationErrorWidget(error: 'Минимум 3 символа')],
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -17,12 +17,10 @@ class _FinishDayButtonWidgetState extends State<FinishDayButtonWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap:
|
||||
() => context.read<DiaryCubit>().finishDiaryDay(
|
||||
onTap: () => context.read<DiaryCubit>().finishDiaryDay(
|
||||
diaryId: widget.diaryDay.id,
|
||||
wakeUp: widget.diaryDay.wakeup.toString(),
|
||||
retire: widget.diaryDay.retire.toString(),
|
||||
comment: widget.diaryDay.comment.isEmpty ? null : widget.diaryDay.comment,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Ink(
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import 'package:easy_debounce/easy_debounce.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_cubit.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_state.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class MealCommentWidget extends StatefulWidget {
|
||||
const MealCommentWidget({super.key, required this.selectedMeal});
|
||||
final int selectedMeal;
|
||||
|
||||
@override
|
||||
State<MealCommentWidget> createState() => _MealCommentWidgetState();
|
||||
}
|
||||
|
||||
class _MealCommentWidgetState extends State<MealCommentWidget> {
|
||||
final controller = TextEditingController();
|
||||
|
||||
@override
|
||||
void didUpdateWidget(MealCommentWidget oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.selectedMeal != oldWidget.selectedMeal) {
|
||||
final state = context.read<ConsultantCommentsCubit>().state;
|
||||
controller.text = state.commentsModel.mealsComments[widget.selectedMeal.toString()]?.comment ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = S.of(context);
|
||||
return BlocBuilder<ConsultantCommentsCubit, ConsultantCommentsState>(
|
||||
builder: (context, state) {
|
||||
final bool hasError;
|
||||
if (state.commentsModel.mealsComments[widget.selectedMeal.toString()] != null &&
|
||||
state.commentsModel.mealsComments[widget.selectedMeal.toString()]!.comment.length < 3) {
|
||||
hasError = true;
|
||||
} else {
|
||||
hasError = false;
|
||||
}
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'${s.commentForMeal} ${widget.selectedMeal}',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyles.textLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
CustomTextFormField(
|
||||
controller: controller,
|
||||
hasError: hasError,
|
||||
onChanged: (value) {
|
||||
EasyDebounce.debounce(
|
||||
'client-typing',
|
||||
const Duration(milliseconds: 500),
|
||||
() => context.read<ConsultantCommentsCubit>().setMealComment(mealNumber: widget.selectedMeal, comment: value),
|
||||
);
|
||||
},
|
||||
hintText: s.typeMealComment,
|
||||
minLines: 4,
|
||||
maxLines: 4,
|
||||
readOnly: false,
|
||||
),
|
||||
if (hasError) ...[const SizedBox(height: 4), const ValidationErrorWidget(error: 'Минимум 3 символа')],
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,24 @@
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.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_button_widget.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/widgets/comment_container.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/widgets/meal_button_widget.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/widgets/meal_comment_widget.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/widgets/mood_rating_widget.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/widgets/photo_gallery_widget.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/widgets/products_list_widget.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/widgets/satiety_rating_widget.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/widgets/time_of_meal_picker_widget.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class MealWidget extends StatefulWidget {
|
||||
const MealWidget({super.key, required this.showMood});
|
||||
const MealWidget({super.key, required this.showMood, required this.showAddButton});
|
||||
final bool showMood;
|
||||
final bool showAddButton;
|
||||
@override
|
||||
State<MealWidget> createState() => _MealWidgetState();
|
||||
}
|
||||
@@ -17,6 +26,7 @@ class MealWidget extends StatefulWidget {
|
||||
class _MealWidgetState extends State<MealWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = S.of(context);
|
||||
return Material(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: Colors.white,
|
||||
@@ -48,8 +58,10 @@ class _MealWidgetState extends State<MealWidget> {
|
||||
const TimeOfMealPickerWidget(),
|
||||
const SizedBox(height: 12),
|
||||
const ProductsListWidget(),
|
||||
const SizedBox(height: 12),
|
||||
const AddProductButtonWidget(),
|
||||
if (widget.showAddButton) ...[
|
||||
const SizedBox(height: 12),
|
||||
const AddProductButtonWidget(),
|
||||
],
|
||||
const SizedBox(height: 12),
|
||||
const PhotoGalleryWidget(),
|
||||
const SizedBox(height: 12),
|
||||
@@ -57,7 +69,35 @@ class _MealWidgetState extends State<MealWidget> {
|
||||
if (widget.showMood) ...[
|
||||
const SizedBox(height: 12),
|
||||
const MoodRatingWidget(),
|
||||
]
|
||||
],
|
||||
BlocBuilder<DiaryCubit, DiaryState>(
|
||||
builder: (context, state) {
|
||||
if (state is DiaryStateLoaded && state.isConsultantView && !state.isDiaryCommented) {
|
||||
return MealCommentWidget(selectedMeal: state.selectedMeal);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
BlocBuilder<DiaryCubit, DiaryState>(
|
||||
builder: (context, state) {
|
||||
if (state is DiaryStateLoaded) {
|
||||
final mealsComments = state.consultantReview?.consultantComments.mealsComments;
|
||||
if (mealsComments != null && (state.selectedMeal - 1 < mealsComments.length)) {
|
||||
return Column(
|
||||
children: [
|
||||
Text(
|
||||
'${s.mealComment} ${state.selectedMeal}',
|
||||
style: TextStyles.textLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
CommentContainer(comment: mealsComments[state.selectedMeal - 1].comment),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -32,9 +32,10 @@ class _MoodRatingWidgetState extends State<MoodRatingWidget> {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
'Ваше настроение после приёма пищи',
|
||||
style: TextStyle(
|
||||
Text(
|
||||
state.isConsultantView ? 'Настроение пользователя после приема пищи' : 'Ваше настроение после приёма пищи',
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
@@ -47,6 +48,7 @@ class _MoodRatingWidgetState extends State<MoodRatingWidget> {
|
||||
width: 32,
|
||||
),
|
||||
RatingBar.builder(
|
||||
ignoreGestures: state.isConsultantView || state.isDiaryFinished || state.isDiaryCommented,
|
||||
initialRating: state.diary.meals[selectedIndex].mood.toDouble() + 1,
|
||||
itemCount: 6,
|
||||
itemSize: MediaQuery.of(context).size.width / 6 - (124 / 6),
|
||||
|
||||
@@ -90,7 +90,7 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
|
||||
builder: (context, state) {
|
||||
if (state is DiaryStateLoaded) {
|
||||
int mealIndex = state.selectedMeal - 1;
|
||||
if (state.diary.meals[mealIndex].photos.isEmpty) {
|
||||
if (state.diary.meals[mealIndex].photos.isEmpty && !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented) {
|
||||
return GestureDetector(
|
||||
onTap: () => showImagePicker(state.diary),
|
||||
child: SvgPicture.asset(
|
||||
@@ -99,6 +99,9 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
|
||||
),
|
||||
);
|
||||
} else {
|
||||
if (state.diary.meals[mealIndex].photos.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -112,30 +115,31 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
|
||||
imageUrl: '$baseUrl/storage/${state.diary.meals[mealIndex].photos[currentSelectedPhotoIndex].path}',
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: IconButton.filled(
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
),
|
||||
onPressed: () async {
|
||||
await diaryCubit.deletePhoto(
|
||||
photo: state.diary.meals[mealIndex].photos[currentSelectedPhotoIndex],
|
||||
diaryId: state.diary.id,
|
||||
);
|
||||
setState(() {
|
||||
if (currentSelectedPhotoIndex == state.diary.meals[mealIndex].photos.length - 1 && currentSelectedPhotoIndex != 0) {
|
||||
currentSelectedPhotoIndex = state.diary.meals[mealIndex].photos.length - 2;
|
||||
}
|
||||
});
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.close_rounded,
|
||||
color: Colors.grey,
|
||||
if (!state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: IconButton.filled(
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
),
|
||||
onPressed: () async {
|
||||
await diaryCubit.deletePhoto(
|
||||
photo: state.diary.meals[mealIndex].photos[currentSelectedPhotoIndex],
|
||||
diaryId: state.diary.id,
|
||||
);
|
||||
setState(() {
|
||||
if (currentSelectedPhotoIndex == state.diary.meals[mealIndex].photos.length - 1 && currentSelectedPhotoIndex != 0) {
|
||||
currentSelectedPhotoIndex = state.diary.meals[mealIndex].photos.length - 2;
|
||||
}
|
||||
});
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.close_rounded,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -147,7 +151,7 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
for (int i = 0; i < state.diary.meals[mealIndex].photos.length + 1; i++) ...[
|
||||
if (i == state.diary.meals[mealIndex].photos.length)
|
||||
if (i == state.diary.meals[mealIndex].photos.length && !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
child: InkWell(
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:easy_debounce/easy_debounce.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/features/diary/models/consultant_comment_model.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_cubit.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_state.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/widgets/comment_container.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class PortionCommentWidget extends StatefulWidget {
|
||||
const PortionCommentWidget({super.key, required this.enabled, required this.portionId, this.comment});
|
||||
final bool enabled;
|
||||
final int portionId;
|
||||
final String? comment;
|
||||
@override
|
||||
State<PortionCommentWidget> createState() => _PortionCommentWidgetState();
|
||||
}
|
||||
|
||||
class _PortionCommentWidgetState extends State<PortionCommentWidget> {
|
||||
final controller = TextEditingController();
|
||||
@override
|
||||
void didUpdateWidget(PortionCommentWidget oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.portionId != oldWidget.portionId) {
|
||||
final state = context.read<ConsultantCommentsCubit>().state;
|
||||
controller.text = state.commentsModel.portions.firstWhereOrNull((portion) => portion.id == widget.portionId)?.comment ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final state = context.read<ConsultantCommentsCubit>().state;
|
||||
controller.text = state.commentsModel.portions.firstWhereOrNull((portion) => portion.id == widget.portionId)?.comment ?? '';
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = S.of(context);
|
||||
if (widget.enabled) {
|
||||
return BlocBuilder<ConsultantCommentsCubit, ConsultantCommentsState>(
|
||||
builder: (context, state) {
|
||||
final bool hasError;
|
||||
if (state.commentsModel.portions.firstWhereOrNull((elem) => elem.id == widget.portionId) != null &&
|
||||
state.commentsModel.portions.firstWhereOrNull((elem) => elem.id == widget.portionId)!.comment.length < 3) {
|
||||
hasError = true;
|
||||
} else {
|
||||
hasError = false;
|
||||
}
|
||||
return Column(
|
||||
children: [
|
||||
CustomTextFormField(
|
||||
controller: controller,
|
||||
hintText: s.typePortionComment,
|
||||
hasError: hasError,
|
||||
onChanged: (value) {
|
||||
EasyDebounce.debounce(
|
||||
'client-typing',
|
||||
const Duration(milliseconds: 500),
|
||||
() {
|
||||
final hasPortionComment = state.commentsModel.portions.any((portion) => portion.id == widget.portionId);
|
||||
if (hasPortionComment) {
|
||||
final portion = state.commentsModel.portions.firstWhere((portion) => portion.id == widget.portionId);
|
||||
context.read<ConsultantCommentsCubit>().updatePortionComment(portion.copyWith(comment: value));
|
||||
return;
|
||||
}
|
||||
context.read<ConsultantCommentsCubit>().addPortionComment(PortionCommentModel(id: widget.portionId, comment: value));
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
if (hasError) ...[const SizedBox(height: 4), const ValidationErrorWidget(error: 'Минимум 3 символа')],
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
if (widget.comment != null) {
|
||||
return CommentContainer(comment: widget.comment ?? '');
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:dotted_border/dotted_border.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/features/consultations/models/consultation_model.dart';
|
||||
import 'package:eatvio_app/features/diary/models/consultant_review_model.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:eatvio_app/features/diary/presentation/widgets/portion_comment_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
@@ -24,7 +28,7 @@ class _ProductWidgetState extends State<ProductWidget> {
|
||||
late final DiaryCubit diaryCubit;
|
||||
late String portionName;
|
||||
late bool isEaten;
|
||||
|
||||
bool showCommentWidget = false;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -55,6 +59,17 @@ class _ProductWidgetState extends State<ProductWidget> {
|
||||
return BlocBuilder<DiaryCubit, DiaryState>(
|
||||
builder: (context, state) {
|
||||
if (state is DiaryStateLoaded) {
|
||||
final isDiaryCommented = state.diary.typeOfDiary.diaryState == DiaryStateEnum.commented ||
|
||||
state.diary.typeOfDiary.diaryState == DiaryStateEnum.lastDayCommented ||
|
||||
state.diary.typeOfDiary.diaryState == DiaryStateEnum.reviewedByUser;
|
||||
final isDiaryFinished =
|
||||
state.diary.typeOfDiary.diaryState == DiaryStateEnum.finished || state.diary.typeOfDiary.diaryState == DiaryStateEnum.lastDayFinished;
|
||||
final canEditPortion = !state.isConsultantView && !isDiaryCommented && !isDiaryFinished;
|
||||
final consultantComments = state.consultantReview?.consultantComments;
|
||||
PortionComment? portionComment;
|
||||
if (consultantComments != null) {
|
||||
portionComment = consultantComments.portionsComments.firstWhereOrNull((elem) => elem.portionId == widget.portion.id);
|
||||
}
|
||||
if (widget.portion.portionState == PortionState.eatenDeleted) {
|
||||
return DottedBorder(
|
||||
dashPattern: const [6, 6],
|
||||
@@ -84,50 +99,74 @@ class _ProductWidgetState extends State<ProductWidget> {
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return ListTile(
|
||||
onTap: () => updateProductInMeal(state, widget.portion),
|
||||
trailing: const 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: const BorderSide(
|
||||
color: Color(0xFF809CB9),
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
horizontalTitleGap: 0,
|
||||
contentPadding: widget.portion.fromDiet ? const 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: const Color(0xFFF6FAFF),
|
||||
Widget? trailing;
|
||||
if (state.isConsultantView && !isDiaryCommented) {
|
||||
trailing = GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
showCommentWidget = !showCommentWidget;
|
||||
});
|
||||
},
|
||||
child: SvgPicture.asset('assets/icons/edit.svg'));
|
||||
} else if (!isDiaryCommented) {
|
||||
trailing = const Icon(Icons.more_vert_rounded);
|
||||
}
|
||||
return Column(
|
||||
children: [
|
||||
ListTile(
|
||||
onTap: canEditPortion ? () => updateProductInMeal(state, widget.portion) : null,
|
||||
trailing: trailing,
|
||||
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: const BorderSide(
|
||||
color: Color(0xFF809CB9),
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
horizontalTitleGap: 0,
|
||||
contentPadding: widget.portion.fromDiet ? const 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: const Color(0xFFF6FAFF),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: PortionCommentWidget(
|
||||
enabled: showCommentWidget,
|
||||
portionId: widget.portion.id,
|
||||
comment: portionComment?.comment,
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ class _ProductsListWidgetState extends State<ProductsListWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<DiaryCubit, DiaryState>(
|
||||
builder: (BuildContext context, DiaryState state) {
|
||||
builder: (BuildContext context, DiaryState state) {
|
||||
if (state is DiaryStateLoaded) {
|
||||
int mealIndex = state.selectedMeal - 1;
|
||||
return Column(
|
||||
|
||||
@@ -32,9 +32,10 @@ class _SatietyRatingWidgetState extends State<SatietyRatingWidget> {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
'Ваша сытость после приёма пищи',
|
||||
style: TextStyle(
|
||||
Text(
|
||||
state.isConsultantView ? 'Сытость пользователя после приема пищи' : 'Ваша сытость после приёма пищи',
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
@@ -47,6 +48,7 @@ class _SatietyRatingWidgetState extends State<SatietyRatingWidget> {
|
||||
width: 32,
|
||||
),
|
||||
RatingBar.builder(
|
||||
ignoreGestures: state.isConsultantView || state.isDiaryFinished || state.isDiaryCommented,
|
||||
initialRating: state.diary.meals[selectedIndex].satiety.toDouble() + 1,
|
||||
itemCount: 6,
|
||||
itemSize: MediaQuery.of(context).size.width / 6 - (124 / 6),
|
||||
@@ -172,6 +174,7 @@ class _SatietyRatingWidgetState extends State<SatietyRatingWidget> {
|
||||
}
|
||||
},
|
||||
onRatingUpdate: (value) {
|
||||
if (state.isConsultantView) return;
|
||||
setState(() {
|
||||
currentSelectedRating[selectedIndex] = value;
|
||||
});
|
||||
|
||||
@@ -69,6 +69,7 @@ class _TimeOfMealPickerWidgetState extends State<TimeOfMealPickerWidget> {
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30)
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -97,7 +98,7 @@ class _TimeOfMealPickerWidgetState extends State<TimeOfMealPickerWidget> {
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () => showTimePicker(state.diary.id),
|
||||
onTap: state.isConsultantView || state.isDiaryFinished || state.isDiaryCommented ? null : () => showTimePicker(state.diary.id),
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
child: Ink(
|
||||
height: 32,
|
||||
|
||||
@@ -8,8 +8,9 @@ import 'package:flutter_svg/flutter_svg.dart';
|
||||
import '../../../../core/theme/theme_helper.dart';
|
||||
|
||||
class WakeupAndRetireWidget extends StatefulWidget {
|
||||
const WakeupAndRetireWidget({super.key, required this.showRetire});
|
||||
const WakeupAndRetireWidget({super.key, required this.showRetire, this.disable = false});
|
||||
final bool showRetire;
|
||||
final bool disable;
|
||||
@override
|
||||
State<WakeupAndRetireWidget> createState() => _WakeupAndRetireWidgetState();
|
||||
}
|
||||
@@ -82,6 +83,7 @@ class _WakeupAndRetireWidgetState extends State<WakeupAndRetireWidget> {
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30)
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -125,7 +127,7 @@ class _WakeupAndRetireWidgetState extends State<WakeupAndRetireWidget> {
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
InkWell(
|
||||
onTap: () => showTimePicker(state.diary.id, true),
|
||||
onTap: widget.disable ? null : () => showTimePicker(state.diary.id, true),
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
child: Ink(
|
||||
height: 32,
|
||||
@@ -185,7 +187,7 @@ class _WakeupAndRetireWidgetState extends State<WakeupAndRetireWidget> {
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
InkWell(
|
||||
onTap: () => showTimePicker(state.diary.id, false),
|
||||
onTap: widget.disable ? null : () => showTimePicker(state.diary.id, false),
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
child: Ink(
|
||||
height: 32,
|
||||
|
||||
Reference in New Issue
Block a user