forked from eatvio/eatvio_app
feat(diary): editing of previous and current finished days
This commit is contained in:
@@ -2,6 +2,7 @@ import 'package:eatvio_app/features/diary/entities/diary_day_entity.dart';
|
|||||||
import 'package:eatvio_app/features/diary/entities/diary_future_diet_day_entity.dart';
|
import 'package:eatvio_app/features/diary/entities/diary_future_diet_day_entity.dart';
|
||||||
import 'package:eatvio_app/features/diary/models/consultant_review_model.dart';
|
import 'package:eatvio_app/features/diary/models/consultant_review_model.dart';
|
||||||
import 'package:eatvio_app/features/diary/models/nutrition_model.dart';
|
import 'package:eatvio_app/features/diary/models/nutrition_model.dart';
|
||||||
|
import 'package:eatvio_app/features/diary/presentation/diary_edit_access.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
part 'diary_state.freezed.dart';
|
part 'diary_state.freezed.dart';
|
||||||
@@ -12,7 +13,8 @@ abstract class DiaryState with _$DiaryState {
|
|||||||
|
|
||||||
const factory DiaryState.loading() = DiaryStateLoading;
|
const factory DiaryState.loading() = DiaryStateLoading;
|
||||||
|
|
||||||
const factory DiaryState.error({required String errorMessage}) = DiaryStateError;
|
const factory DiaryState.error({required String errorMessage}) =
|
||||||
|
DiaryStateError;
|
||||||
|
|
||||||
const factory DiaryState.loaded({
|
const factory DiaryState.loaded({
|
||||||
required DiaryDayEntity diary,
|
required DiaryDayEntity diary,
|
||||||
@@ -31,6 +33,19 @@ abstract class DiaryState with _$DiaryState {
|
|||||||
@Default(false) bool isLoadingAddingProductInMeal,
|
@Default(false) bool isLoadingAddingProductInMeal,
|
||||||
}) = DiaryStateLoaded;
|
}) = DiaryStateLoaded;
|
||||||
|
|
||||||
const factory DiaryState.loadedTomorrowWithDiet({required DiaryFutureDietDayEntity diary, required DateTime selectedDay, required int selectedMeal}) =
|
const factory DiaryState.loadedTomorrowWithDiet({
|
||||||
DiaryStateLoadedTomorrowWithDiet;
|
required DiaryFutureDietDayEntity diary,
|
||||||
|
required DateTime selectedDay,
|
||||||
|
required int selectedMeal,
|
||||||
|
}) = DiaryStateLoadedTomorrowWithDiet;
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DiaryStateLoadedEditAccess on DiaryStateLoaded {
|
||||||
|
bool get canEditDiary => canEditDiaryDay(
|
||||||
|
isConsultantView: isConsultantView,
|
||||||
|
isDiaryCommented: isDiaryCommented,
|
||||||
|
isDiaryFinished: isDiaryFinished,
|
||||||
|
closed: diary.closed,
|
||||||
|
diaryDate: diary.date,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
38
lib/features/diary/presentation/diary_edit_access.dart
Normal file
38
lib/features/diary/presentation/diary_edit_access.dart
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/// Finished and last-day-finished diaries stay editable on the calendar day
|
||||||
|
/// itself and on the next calendar day. Older finished days stay read-only.
|
||||||
|
const int diaryFinishedEditWindowInDays = 1;
|
||||||
|
|
||||||
|
bool canEditDiaryDay({
|
||||||
|
required bool isConsultantView,
|
||||||
|
required bool isDiaryCommented,
|
||||||
|
required bool isDiaryFinished,
|
||||||
|
required int closed,
|
||||||
|
required String diaryDate,
|
||||||
|
DateTime? now,
|
||||||
|
}) {
|
||||||
|
if (isConsultantView || isDiaryCommented || closed != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!isDiaryFinished) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
final day = _calendarDate(diaryDate);
|
||||||
|
if (day == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final clock = now ?? DateTime.now();
|
||||||
|
final today = DateTime(clock.year, clock.month, clock.day);
|
||||||
|
final oldestEditableDay = today.subtract(
|
||||||
|
const Duration(days: diaryFinishedEditWindowInDays),
|
||||||
|
);
|
||||||
|
return !day.isBefore(oldestEditableDay) && !day.isAfter(today);
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime? _calendarDate(String raw) {
|
||||||
|
final parsed = DateTime.tryParse(raw);
|
||||||
|
if (parsed == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final local = parsed.toLocal();
|
||||||
|
return DateTime(local.year, local.month, local.day);
|
||||||
|
}
|
||||||
@@ -74,7 +74,12 @@ class _DiaryScreenContentState extends State<DiaryScreenContent> {
|
|||||||
}
|
}
|
||||||
if (state is DiaryStateError) {
|
if (state is DiaryStateError) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: DataErrorWidget(onReload: () => context.read<DiaryCubit>().fetchDiaryV2(needLoading: false, diaryId: widget.diaryId)),
|
body: DataErrorWidget(
|
||||||
|
onReload: () => context.read<DiaryCubit>().fetchDiaryV2(
|
||||||
|
needLoading: false,
|
||||||
|
diaryId: widget.diaryId,
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,8 +89,14 @@ class _DiaryScreenContentState extends State<DiaryScreenContent> {
|
|||||||
? AppBar(toolbarHeight: 0)
|
? AppBar(toolbarHeight: 0)
|
||||||
: AppBar(
|
: AppBar(
|
||||||
title: Text(
|
title: Text(
|
||||||
state is DiaryStateLoaded ? DateTime.parse(state.diary.date).toLocal().format('d MMMM') : '',
|
state is DiaryStateLoaded
|
||||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
? DateTime.parse(
|
||||||
|
state.diary.date,
|
||||||
|
).toLocal().format('d MMMM')
|
||||||
|
: '',
|
||||||
|
style: TextStyles.headingXLSemibold.copyWith(
|
||||||
|
color: appTheme.black,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
),
|
),
|
||||||
@@ -95,26 +106,39 @@ class _DiaryScreenContentState extends State<DiaryScreenContent> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
if (widget.diaryId == null) CalendarWidget(date: widget.date),
|
if (widget.diaryId == null) CalendarWidget(date: widget.date),
|
||||||
if (state is DiaryStateLoading) ...[const SizedBox(height: 60), const Loader()],
|
if (state is DiaryStateLoading) ...[
|
||||||
|
const SizedBox(height: 60),
|
||||||
|
const Loader(),
|
||||||
|
],
|
||||||
if (state is DiaryStateLoaded)
|
if (state is DiaryStateLoaded)
|
||||||
WakeupAndRetireWidget(
|
WakeupAndRetireWidget(
|
||||||
isEnabled: state.diary.typeOfDiary.hasChosenPlan || state.isConsultantView || state.isWakeUpAndRetireEnabled,
|
isEnabled:
|
||||||
disable: state.isConsultantView || state.isDiaryFinished || state.isDiaryCommented,
|
state.diary.typeOfDiary.hasChosenPlan ||
|
||||||
|
state.isConsultantView ||
|
||||||
|
state.isWakeUpAndRetireEnabled,
|
||||||
|
disable: !state.canEditDiary,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
if (state is DiaryStateLoaded)
|
if (state is DiaryStateLoaded)
|
||||||
MealWidget(
|
MealWidget(
|
||||||
showMood: state.diary.typeOfDiary.hasChosenPlan || state.isConsultantView || state.isMoodEnabled,
|
showMood:
|
||||||
showAddButton: !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented,
|
state.diary.typeOfDiary.hasChosenPlan ||
|
||||||
|
state.isConsultantView ||
|
||||||
|
state.isMoodEnabled,
|
||||||
|
showAddButton: state.canEditDiary,
|
||||||
),
|
),
|
||||||
if (state is DiaryStateLoadedTomorrowWithDiet) const MealWidget(showMood: false, showAddButton: false),
|
if (state is DiaryStateLoadedTomorrowWithDiet)
|
||||||
|
const MealWidget(showMood: false, showAddButton: false),
|
||||||
if (state is DiaryStateLoaded) const CaloriesChartWidget(),
|
if (state is DiaryStateLoaded) const CaloriesChartWidget(),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
Builder(
|
Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
if (state is DiaryStateLoaded && state.diary.typeOfDiary.hasChosenPlan) {
|
if (state is DiaryStateLoaded &&
|
||||||
|
state.diary.typeOfDiary.hasChosenPlan) {
|
||||||
if (state.diary.comment.isNotEmpty) {
|
if (state.diary.comment.isNotEmpty) {
|
||||||
return CommentForConsultantWidget(comment: state.diary.comment);
|
return CommentForConsultantWidget(
|
||||||
|
comment: state.diary.comment,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (state.isDiaryCommented || state.isDiaryFinished) {
|
if (state.isDiaryCommented || state.isDiaryFinished) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
@@ -124,7 +148,10 @@ class _DiaryScreenContentState extends State<DiaryScreenContent> {
|
|||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (state is DiaryStateLoaded && !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented)
|
if (state is DiaryStateLoaded &&
|
||||||
|
!state.isConsultantView &&
|
||||||
|
!state.isDiaryFinished &&
|
||||||
|
!state.isDiaryCommented)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 12),
|
padding: const EdgeInsets.only(top: 12),
|
||||||
child: _isDiaryFinishing
|
child: _isDiaryFinishing
|
||||||
@@ -135,11 +162,13 @@ class _DiaryScreenContentState extends State<DiaryScreenContent> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_isDiaryFinishing = true;
|
_isDiaryFinishing = true;
|
||||||
});
|
});
|
||||||
final isFinished = await context.read<DiaryCubit>().finishDiaryDay(
|
final isFinished = await context
|
||||||
diaryId: state.diary.id,
|
.read<DiaryCubit>()
|
||||||
wakeUp: state.diary.wakeup.toString(),
|
.finishDiaryDay(
|
||||||
retire: state.diary.retire.toString(),
|
diaryId: state.diary.id,
|
||||||
);
|
wakeUp: state.diary.wakeup.toString(),
|
||||||
|
retire: state.diary.retire.toString(),
|
||||||
|
);
|
||||||
if (!isFinished && context.mounted) {
|
if (!isFinished && context.mounted) {
|
||||||
showErrorDialog(context);
|
showErrorDialog(context);
|
||||||
}
|
}
|
||||||
@@ -149,33 +178,64 @@ class _DiaryScreenContentState extends State<DiaryScreenContent> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (state is DiaryStateLoaded && state.isConsultantView && !state.isDiaryCommented) ...[
|
if (state is DiaryStateLoaded &&
|
||||||
|
state.isConsultantView &&
|
||||||
|
!state.isDiaryCommented) ...[
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
const MainBackgroundBouble(padding: EdgeInsets.all(16), child: DayCommentWidget()),
|
const MainBackgroundBouble(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
child: DayCommentWidget(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
if (state is DiaryStateLoaded && state.consultantReview?.consultantComments.dayComment != null) ...[
|
if (state is DiaryStateLoaded &&
|
||||||
|
state.consultantReview?.consultantComments.dayComment !=
|
||||||
|
null) ...[
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
MainBackgroundBouble(
|
MainBackgroundBouble(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Text(s.dayComment, style: TextStyles.textLSemibold.copyWith(color: appTheme.black)),
|
Text(
|
||||||
|
s.dayComment,
|
||||||
|
style: TextStyles.textLSemibold.copyWith(
|
||||||
|
color: appTheme.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
CommentContainer(comment: state.consultantReview?.consultantComments.dayComment ?? ''),
|
CommentContainer(
|
||||||
|
comment:
|
||||||
|
state
|
||||||
|
.consultantReview
|
||||||
|
?.consultantComments
|
||||||
|
.dayComment ??
|
||||||
|
'',
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 28),
|
const SizedBox(height: 28),
|
||||||
],
|
],
|
||||||
if (state is DiaryStateLoaded && state.isConsultantView && state.isDiaryFinished && !state.isDiaryCommented) ...[
|
if (state is DiaryStateLoaded &&
|
||||||
|
state.isConsultantView &&
|
||||||
|
state.isDiaryFinished &&
|
||||||
|
!state.isDiaryCommented) ...[
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
CustomOutlinedButton(
|
CustomOutlinedButton(
|
||||||
text: s.send,
|
text: s.send,
|
||||||
height: 52,
|
height: 52,
|
||||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
buttonTextStyle: TextStyles.textMSemibold.copyWith(
|
||||||
buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.blueA400)),
|
color: appTheme.whiteA700,
|
||||||
|
),
|
||||||
|
buttonStyle: ButtonStyle(
|
||||||
|
side: const WidgetStatePropertyAll(BorderSide.none),
|
||||||
|
backgroundColor: WidgetStatePropertyAll(
|
||||||
|
appTheme.blueA400,
|
||||||
|
),
|
||||||
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await context.read<ConsultantCommentsCubit>().submitComments(state.diary.id);
|
await context
|
||||||
|
.read<ConsultantCommentsCubit>()
|
||||||
|
.submitComments(state.diary.id);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ class _MoodRatingWidgetState extends State<MoodRatingWidget> {
|
|||||||
final meals = state.diary.meals;
|
final meals = state.diary.meals;
|
||||||
if (selectedIndex >= 0 && selectedIndex < meals.length) {
|
if (selectedIndex >= 0 && selectedIndex < meals.length) {
|
||||||
if (isFirstFetch[selectedIndex]) {
|
if (isFirstFetch[selectedIndex]) {
|
||||||
currentSelectedRating[selectedIndex] = meals[selectedIndex].mood.toDouble() + 1;
|
currentSelectedRating[selectedIndex] =
|
||||||
|
meals[selectedIndex].mood.toDouble() + 1;
|
||||||
isFirstFetch[selectedIndex] = false;
|
isFirstFetch[selectedIndex] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,21 +37,30 @@ class _MoodRatingWidgetState extends State<MoodRatingWidget> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
state.isConsultantView ? 'Настроение пользователя после приема пищи' : 'Ваше настроение после приёма пищи',
|
state.isConsultantView
|
||||||
|
? 'Настроение пользователя после приема пищи'
|
||||||
|
: 'Ваше настроение после приёма пищи',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(fontSize: 16),
|
style: const TextStyle(fontSize: 16),
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
SvgPicture.asset('assets/icons/mood_1.svg', height: 32, width: 32),
|
SvgPicture.asset(
|
||||||
|
'assets/icons/mood_1.svg',
|
||||||
|
height: 32,
|
||||||
|
width: 32,
|
||||||
|
),
|
||||||
RatingBar.builder(
|
RatingBar.builder(
|
||||||
ignoreGestures: state.isConsultantView || state.isDiaryFinished || state.isDiaryCommented,
|
ignoreGestures: !state.canEditDiary,
|
||||||
initialRating: (selectedIndex >= 0 && selectedIndex < state.diary.meals.length)
|
initialRating:
|
||||||
|
(selectedIndex >= 0 &&
|
||||||
|
selectedIndex < state.diary.meals.length)
|
||||||
? state.diary.meals[selectedIndex].mood.toDouble() + 1
|
? state.diary.meals[selectedIndex].mood.toDouble() + 1
|
||||||
: 3,
|
: 3,
|
||||||
itemCount: 6,
|
itemCount: 6,
|
||||||
itemSize: MediaQuery.of(context).size.width / 6 - (124 / 6),
|
itemSize:
|
||||||
|
MediaQuery.of(context).size.width / 6 - (124 / 6),
|
||||||
updateOnDrag: true,
|
updateOnDrag: true,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
switch (index) {
|
switch (index) {
|
||||||
@@ -58,82 +68,154 @@ class _MoodRatingWidgetState extends State<MoodRatingWidget> {
|
|||||||
return Container(
|
return Container(
|
||||||
width: MediaQuery.of(context).size.width / 6,
|
width: MediaQuery.of(context).size.width / 6,
|
||||||
height: 24,
|
height: 24,
|
||||||
margin: currentSelectedRating[selectedIndex] == 1 ? const EdgeInsets.symmetric(horizontal: 2) : null,
|
margin: currentSelectedRating[selectedIndex] == 1
|
||||||
|
? const EdgeInsets.symmetric(horizontal: 2)
|
||||||
|
: null,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFC82222),
|
color: const Color(0xFFC82222),
|
||||||
border: currentSelectedRating[selectedIndex] == 1
|
border:
|
||||||
? Border.all(width: 2, color: const Color(0xFFAA3333), strokeAlign: BorderSide.strokeAlignOutside)
|
currentSelectedRating[selectedIndex] == 1
|
||||||
|
? Border.all(
|
||||||
|
width: 2,
|
||||||
|
color: const Color(0xFFAA3333),
|
||||||
|
strokeAlign:
|
||||||
|
BorderSide.strokeAlignOutside,
|
||||||
|
)
|
||||||
: null,
|
: null,
|
||||||
borderRadius: currentSelectedRating[selectedIndex] == 1
|
borderRadius:
|
||||||
? const BorderRadius.horizontal(left: Radius.circular(8), right: Radius.circular(2))
|
currentSelectedRating[selectedIndex] == 1
|
||||||
: const BorderRadius.horizontal(left: Radius.circular(8)),
|
? const BorderRadius.horizontal(
|
||||||
|
left: Radius.circular(8),
|
||||||
|
right: Radius.circular(2),
|
||||||
|
)
|
||||||
|
: const BorderRadius.horizontal(
|
||||||
|
left: Radius.circular(8),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
case 1:
|
case 1:
|
||||||
return Container(
|
return Container(
|
||||||
width: MediaQuery.of(context).size.width / 6,
|
width: MediaQuery.of(context).size.width / 6,
|
||||||
height: 24,
|
height: 24,
|
||||||
margin: currentSelectedRating[selectedIndex] == 2 ? const EdgeInsets.symmetric(horizontal: 2) : null,
|
margin: currentSelectedRating[selectedIndex] == 2
|
||||||
|
? const EdgeInsets.symmetric(horizontal: 2)
|
||||||
|
: null,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFFF3E3E),
|
color: const Color(0xFFFF3E3E),
|
||||||
border: currentSelectedRating[selectedIndex] == 2
|
border:
|
||||||
? Border.all(width: 2, color: const Color(0xFF550000), strokeAlign: BorderSide.strokeAlignOutside)
|
currentSelectedRating[selectedIndex] == 2
|
||||||
|
? Border.all(
|
||||||
|
width: 2,
|
||||||
|
color: const Color(0xFF550000),
|
||||||
|
strokeAlign:
|
||||||
|
BorderSide.strokeAlignOutside,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
borderRadius:
|
||||||
|
currentSelectedRating[selectedIndex] == 2
|
||||||
|
? BorderRadius.circular(2)
|
||||||
: null,
|
: null,
|
||||||
borderRadius: currentSelectedRating[selectedIndex] == 2 ? BorderRadius.circular(2) : null,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
case 2:
|
case 2:
|
||||||
return Container(
|
return Container(
|
||||||
width: MediaQuery.of(context).size.width / 6,
|
width: MediaQuery.of(context).size.width / 6,
|
||||||
height: 24,
|
height: 24,
|
||||||
margin: currentSelectedRating[selectedIndex] == 3 ? const EdgeInsets.symmetric(horizontal: 2) : null,
|
margin: currentSelectedRating[selectedIndex] == 3
|
||||||
|
? const EdgeInsets.symmetric(horizontal: 2)
|
||||||
|
: null,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFF8C926),
|
color: const Color(0xFFF8C926),
|
||||||
border: currentSelectedRating[selectedIndex] == 3
|
border:
|
||||||
? Border.all(width: 2, color: const Color(0xFF8C5D00), strokeAlign: BorderSide.strokeAlignOutside)
|
currentSelectedRating[selectedIndex] == 3
|
||||||
|
? Border.all(
|
||||||
|
width: 2,
|
||||||
|
color: const Color(0xFF8C5D00),
|
||||||
|
strokeAlign:
|
||||||
|
BorderSide.strokeAlignOutside,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
borderRadius:
|
||||||
|
currentSelectedRating[selectedIndex] == 3
|
||||||
|
? BorderRadius.circular(2)
|
||||||
: null,
|
: null,
|
||||||
borderRadius: currentSelectedRating[selectedIndex] == 3 ? BorderRadius.circular(2) : null,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
case 3:
|
case 3:
|
||||||
return Container(
|
return Container(
|
||||||
width: MediaQuery.of(context).size.width / 6,
|
width: MediaQuery.of(context).size.width / 6,
|
||||||
height: 24,
|
height: 24,
|
||||||
margin: currentSelectedRating[selectedIndex] == 4 ? const EdgeInsets.symmetric(horizontal: 2) : null,
|
margin: currentSelectedRating[selectedIndex] == 4
|
||||||
|
? const EdgeInsets.symmetric(horizontal: 2)
|
||||||
|
: null,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFFFF3BF),
|
color: const Color(0xFFFFF3BF),
|
||||||
border: currentSelectedRating[selectedIndex] == 4
|
border:
|
||||||
? Border.all(width: 2, color: const Color(0xFFB5911B), strokeAlign: BorderSide.strokeAlignOutside)
|
currentSelectedRating[selectedIndex] == 4
|
||||||
|
? Border.all(
|
||||||
|
width: 2,
|
||||||
|
color: const Color(0xFFB5911B),
|
||||||
|
strokeAlign:
|
||||||
|
BorderSide.strokeAlignOutside,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
borderRadius:
|
||||||
|
currentSelectedRating[selectedIndex] == 4
|
||||||
|
? BorderRadius.circular(2)
|
||||||
: null,
|
: null,
|
||||||
borderRadius: currentSelectedRating[selectedIndex] == 4 ? BorderRadius.circular(2) : null,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
case 4:
|
case 4:
|
||||||
return Container(
|
return Container(
|
||||||
width: MediaQuery.of(context).size.width / 6,
|
width: MediaQuery.of(context).size.width / 6,
|
||||||
height: 24,
|
height: 24,
|
||||||
margin: currentSelectedRating[selectedIndex] == 5 ? const EdgeInsets.symmetric(horizontal: 2) : null,
|
margin: currentSelectedRating[selectedIndex] == 5
|
||||||
|
? const EdgeInsets.symmetric(horizontal: 2)
|
||||||
|
: null,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFF62E833),
|
color: const Color(0xFF62E833),
|
||||||
border: currentSelectedRating[selectedIndex] == 5
|
border:
|
||||||
? Border.all(width: 2, color: const Color(0xFF276918), strokeAlign: BorderSide.strokeAlignOutside)
|
currentSelectedRating[selectedIndex] == 5
|
||||||
|
? Border.all(
|
||||||
|
width: 2,
|
||||||
|
color: const Color(0xFF276918),
|
||||||
|
strokeAlign:
|
||||||
|
BorderSide.strokeAlignOutside,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
borderRadius:
|
||||||
|
currentSelectedRating[selectedIndex] == 5
|
||||||
|
? BorderRadius.circular(2)
|
||||||
: null,
|
: null,
|
||||||
borderRadius: currentSelectedRating[selectedIndex] == 5 ? BorderRadius.circular(2) : null,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return Container(
|
return Container(
|
||||||
width: MediaQuery.of(context).size.width / 6,
|
width: MediaQuery.of(context).size.width / 6,
|
||||||
height: 24,
|
height: 24,
|
||||||
margin: currentSelectedRating[selectedIndex] == 6 ? const EdgeInsets.symmetric(horizontal: 2) : null,
|
margin: currentSelectedRating[selectedIndex] == 6
|
||||||
|
? const EdgeInsets.symmetric(horizontal: 2)
|
||||||
|
: null,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFF4DCE20),
|
color: const Color(0xFF4DCE20),
|
||||||
border: currentSelectedRating[selectedIndex] == 6
|
border:
|
||||||
? Border.all(width: 2, color: const Color(0xFF184D12), strokeAlign: BorderSide.strokeAlignOutside)
|
currentSelectedRating[selectedIndex] == 6
|
||||||
|
? Border.all(
|
||||||
|
width: 2,
|
||||||
|
color: const Color(0xFF184D12),
|
||||||
|
strokeAlign:
|
||||||
|
BorderSide.strokeAlignOutside,
|
||||||
|
)
|
||||||
: null,
|
: null,
|
||||||
borderRadius: currentSelectedRating[selectedIndex] == 6
|
borderRadius:
|
||||||
? const BorderRadius.horizontal(left: Radius.circular(2), right: Radius.circular(8))
|
currentSelectedRating[selectedIndex] == 6
|
||||||
: const BorderRadius.horizontal(right: Radius.circular(8)),
|
? const BorderRadius.horizontal(
|
||||||
|
left: Radius.circular(2),
|
||||||
|
right: Radius.circular(8),
|
||||||
|
)
|
||||||
|
: const BorderRadius.horizontal(
|
||||||
|
right: Radius.circular(8),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -142,12 +224,25 @@ class _MoodRatingWidgetState extends State<MoodRatingWidget> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
currentSelectedRating[selectedIndex] = value;
|
currentSelectedRating[selectedIndex] = value;
|
||||||
});
|
});
|
||||||
EasyDebounce.debounce('change-satiety', const Duration(milliseconds: 250), () {
|
EasyDebounce.debounce(
|
||||||
context.read<DiaryCubit>().fillMood(diaryId: state.diary.id, mood: currentSelectedRating[selectedIndex].toInt() - 1);
|
'change-satiety',
|
||||||
});
|
const Duration(milliseconds: 250),
|
||||||
|
() {
|
||||||
|
context.read<DiaryCubit>().fillMood(
|
||||||
|
diaryId: state.diary.id,
|
||||||
|
mood:
|
||||||
|
currentSelectedRating[selectedIndex].toInt() -
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SvgPicture.asset('assets/icons/mood_2.svg', height: 32, width: 32),
|
SvgPicture.asset(
|
||||||
|
'assets/icons/mood_2.svg',
|
||||||
|
height: 32,
|
||||||
|
width: 32,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -52,7 +52,9 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
|
|||||||
AppRouter.navigatorKey.currentContext?.pop();
|
AppRouter.navigatorKey.currentContext?.pop();
|
||||||
|
|
||||||
final picker = ImagePicker();
|
final picker = ImagePicker();
|
||||||
XFile? image = await picker.pickImage(source: ImageSource.camera);
|
XFile? image = await picker.pickImage(
|
||||||
|
source: ImageSource.camera,
|
||||||
|
);
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
diaryCubit.uploadPhotos(
|
diaryCubit.uploadPhotos(
|
||||||
files: [File(image.path)],
|
files: [File(image.path)],
|
||||||
@@ -116,7 +118,8 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
|
|||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state is DiaryStateLoaded) {
|
if (state is DiaryStateLoaded) {
|
||||||
int mealIndex = state.selectedMeal - 1;
|
int mealIndex = state.selectedMeal - 1;
|
||||||
if (state.diary.meals[mealIndex].photos.isEmpty && !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented) {
|
if (state.diary.meals[mealIndex].photos.isEmpty &&
|
||||||
|
state.canEditDiary) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
showImagePicker(state.diary);
|
showImagePicker(state.diary);
|
||||||
@@ -140,10 +143,11 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
|
|||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
child: CachedNetworkImage(
|
child: CachedNetworkImage(
|
||||||
imageUrl: '${sl<AppConfig>().baseUrl}/storage/${state.diary.meals[mealIndex].photos[currentSelectedPhotoIndex].path}',
|
imageUrl:
|
||||||
|
'${sl<AppConfig>().baseUrl}/storage/${state.diary.meals[mealIndex].photos[currentSelectedPhotoIndex].path}',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (!state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented)
|
if (state.canEditDiary)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
child: IconButton.filled(
|
child: IconButton.filled(
|
||||||
@@ -153,12 +157,24 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
|
|||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await diaryCubit.deletePhoto(
|
await diaryCubit.deletePhoto(
|
||||||
photo: state.diary.meals[mealIndex].photos[currentSelectedPhotoIndex],
|
photo: state
|
||||||
|
.diary
|
||||||
|
.meals[mealIndex]
|
||||||
|
.photos[currentSelectedPhotoIndex],
|
||||||
diaryId: state.diary.id,
|
diaryId: state.diary.id,
|
||||||
);
|
);
|
||||||
setState(() {
|
setState(() {
|
||||||
if (currentSelectedPhotoIndex == state.diary.meals[mealIndex].photos.length - 1 && currentSelectedPhotoIndex != 0) {
|
if (currentSelectedPhotoIndex ==
|
||||||
currentSelectedPhotoIndex = state.diary.meals[mealIndex].photos.length - 2;
|
state
|
||||||
|
.diary
|
||||||
|
.meals[mealIndex]
|
||||||
|
.photos
|
||||||
|
.length -
|
||||||
|
1 &&
|
||||||
|
currentSelectedPhotoIndex != 0) {
|
||||||
|
currentSelectedPhotoIndex =
|
||||||
|
state.diary.meals[mealIndex].photos.length -
|
||||||
|
2;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -178,8 +194,13 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
for (int i = 0; i < state.diary.meals[mealIndex].photos.length + 1; i++) ...[
|
for (
|
||||||
if (i == state.diary.meals[mealIndex].photos.length && !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented)
|
int i = 0;
|
||||||
|
i < state.diary.meals[mealIndex].photos.length + 1;
|
||||||
|
i++
|
||||||
|
) ...[
|
||||||
|
if (i == state.diary.meals[mealIndex].photos.length &&
|
||||||
|
state.canEditDiary)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 8),
|
padding: const EdgeInsets.only(left: 8),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
@@ -212,13 +233,15 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
|
|||||||
height: 52,
|
height: 52,
|
||||||
width: 52,
|
width: 52,
|
||||||
child: CachedNetworkImage(
|
child: CachedNetworkImage(
|
||||||
imageUrl: '${sl<AppConfig>().baseUrl}/storage/${state.diary.meals[mealIndex].photos[i].path}',
|
imageUrl:
|
||||||
|
'${sl<AppConfig>().baseUrl}/storage/${state.diary.meals[mealIndex].photos[i].path}',
|
||||||
fit: BoxFit.fitWidth,
|
fit: BoxFit.fitWidth,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (i < state.diary.meals[mealIndex].photos.length - 1) const SizedBox(width: 8),
|
if (i < state.diary.meals[mealIndex].photos.length - 1)
|
||||||
|
const SizedBox(width: 8),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:dotted_border/dotted_border.dart';
|
import 'package:dotted_border/dotted_border.dart';
|
||||||
import 'package:eatvio_app/core/theme/theme_helper.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/consultant_review_model.dart';
|
||||||
import 'package:eatvio_app/features/diary/models/diary_day_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_cubit.dart';
|
||||||
@@ -32,17 +30,26 @@ class _ProductWidgetState extends State<ProductWidget> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
diaryCubit = context.read<DiaryCubit>();
|
diaryCubit = context.read<DiaryCubit>();
|
||||||
portionName = widget.portion.portion == 'gr' ? 'гр' : 'шт';
|
portionName = widget.portion.portion == 'gr' ? 'гр' : 'шт';
|
||||||
isEaten = widget.portion.portionState == PortionState.eatenFromDiet ? true : false;
|
isEaten = widget.portion.portionState == PortionState.eatenFromDiet
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildNutritionalInfo() {
|
Widget _buildNutritionalInfo() {
|
||||||
if (widget.portion.productNutritionalValueId == null) {
|
if (widget.portion.productNutritionalValueId == null) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(12)),
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
child: const Text(
|
child: const Text(
|
||||||
'Продукт отправлен на модерацию',
|
'Продукт отправлен на модерацию',
|
||||||
style: TextStyle(color: Color(0xFF2F80ED), fontSize: 12, fontWeight: FontWeight.w500),
|
style: TextStyle(
|
||||||
|
color: Color(0xFF2F80ED),
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -57,7 +64,8 @@ class _ProductWidgetState extends State<ProductWidget> {
|
|||||||
calories = (caloriesInfo.calories * widget.portion.quantity) / 100;
|
calories = (caloriesInfo.calories * widget.portion.quantity) / 100;
|
||||||
proteins = (caloriesInfo.proteins * widget.portion.quantity) / 100;
|
proteins = (caloriesInfo.proteins * widget.portion.quantity) / 100;
|
||||||
fats = (caloriesInfo.fats * widget.portion.quantity) / 100;
|
fats = (caloriesInfo.fats * widget.portion.quantity) / 100;
|
||||||
carbohydrates = (caloriesInfo.carbohydrates * widget.portion.quantity) / 100;
|
carbohydrates =
|
||||||
|
(caloriesInfo.carbohydrates * widget.portion.quantity) / 100;
|
||||||
} else {
|
} else {
|
||||||
calories = caloriesInfo.energyOnPortion;
|
calories = caloriesInfo.energyOnPortion;
|
||||||
proteins = caloriesInfo.proteinsOnPortion;
|
proteins = caloriesInfo.proteinsOnPortion;
|
||||||
@@ -81,10 +89,17 @@ class _ProductWidgetState extends State<ProductWidget> {
|
|||||||
Widget _buildNutritionChip(String label, int value) {
|
Widget _buildNutritionChip(String label, int value) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||||
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(8)),
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'$label $value',
|
'$label $value',
|
||||||
style: const TextStyle(color: Color(0xFF6D6F71), fontSize: 12, fontWeight: FontWeight.w500),
|
style: const TextStyle(
|
||||||
|
color: Color(0xFF6D6F71),
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -93,16 +108,24 @@ class _ProductWidgetState extends State<ProductWidget> {
|
|||||||
void didUpdateWidget(ProductWidget oldWidget) {
|
void didUpdateWidget(ProductWidget oldWidget) {
|
||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
portionName = widget.portion.portion == 'gr' ? 'гр' : 'шт';
|
portionName = widget.portion.portion == 'gr' ? 'гр' : 'шт';
|
||||||
isEaten = widget.portion.portionState == PortionState.eatenFromDiet ? true : false;
|
isEaten = widget.portion.portionState == PortionState.eatenFromDiet
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future updateProductInMeal(DiaryStateLoaded state, PortionModel portion) {
|
Future updateProductInMeal(DiaryStateLoaded state, PortionModel portion) {
|
||||||
return showModalBottomSheet(
|
return showModalBottomSheet(
|
||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
context: context,
|
context: context,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(12))),
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
|
||||||
|
),
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
builder: (context) => AddProductModal(state: state, portion: portion, diaryCubit: diaryCubit),
|
builder: (context) => AddProductModal(
|
||||||
|
state: state,
|
||||||
|
portion: portion,
|
||||||
|
diaryCubit: diaryCubit,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,33 +136,36 @@ class _ProductWidgetState extends State<ProductWidget> {
|
|||||||
if (state is DiaryStateLoadedTomorrowWithDiet) {
|
if (state is DiaryStateLoadedTomorrowWithDiet) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
horizontalTitleGap: 0,
|
horizontalTitleGap: 0,
|
||||||
contentPadding: widget.portion.fromDiet ? const EdgeInsets.only(right: 24, left: 12) : null,
|
contentPadding: widget.portion.fromDiet
|
||||||
|
? const EdgeInsets.only(right: 24, left: 12)
|
||||||
|
: null,
|
||||||
title: Text(widget.portion.productName),
|
title: Text(widget.portion.productName),
|
||||||
subtitle: Column(
|
subtitle: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text('${widget.portion.quantity} ${widget.portion.portion == 'gr' ? 'гр' : 'шт'}'),
|
Text(
|
||||||
|
'${widget.portion.quantity} ${widget.portion.portion == 'gr' ? 'гр' : 'шт'}',
|
||||||
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
_buildNutritionalInfo(),
|
_buildNutritionalInfo(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
tileColor: const Color(0xFFF6FAFF),
|
tileColor: const Color(0xFFF6FAFF),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (state is DiaryStateLoaded) {
|
if (state is DiaryStateLoaded) {
|
||||||
final isDiaryCommented =
|
final canEditPortion = state.canEditDiary && !widget.portion.fromDiet;
|
||||||
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 && !widget.portion.fromDiet;
|
|
||||||
final consultantComments = state.consultantReview?.consultantComments;
|
final consultantComments = state.consultantReview?.consultantComments;
|
||||||
final canMarkAsEaten = !state.isConsultantView && !isDiaryCommented && !isDiaryFinished;
|
final canMarkAsEaten = state.canEditDiary;
|
||||||
PortionComment? portionComment;
|
PortionComment? portionComment;
|
||||||
if (consultantComments != null) {
|
if (consultantComments != null) {
|
||||||
portionComment = consultantComments.portionsComments.firstWhereOrNull((elem) => elem.portionId == widget.portion.id);
|
portionComment = consultantComments.portionsComments
|
||||||
|
.firstWhereOrNull(
|
||||||
|
(elem) => elem.portionId == widget.portion.id,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (widget.portion.portionState == PortionState.eatenDeleted) {
|
if (widget.portion.portionState == PortionState.eatenDeleted) {
|
||||||
return DottedBorder(
|
return DottedBorder(
|
||||||
@@ -151,27 +177,41 @@ class _ProductWidgetState extends State<ProductWidget> {
|
|||||||
color: Color(0xFF6D6F71),
|
color: Color(0xFF6D6F71),
|
||||||
),
|
),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
onTap: () => diaryCubit.restore(diaryId: state.diary.id, mealId: state.selectedMeal, portionId: widget.portion.id),
|
onTap: state.canEditDiary
|
||||||
trailing: SvgPicture.asset('assets/icons/revert_product.svg'),
|
? () => diaryCubit.restore(
|
||||||
|
diaryId: state.diary.id,
|
||||||
|
mealId: state.selectedMeal,
|
||||||
|
portionId: widget.portion.id,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
trailing: state.canEditDiary
|
||||||
|
? SvgPicture.asset('assets/icons/revert_product.svg')
|
||||||
|
: null,
|
||||||
horizontalTitleGap: 0,
|
horizontalTitleGap: 0,
|
||||||
contentPadding: widget.portion.fromDiet ? const EdgeInsets.only(right: 24, left: 4) : null,
|
contentPadding: widget.portion.fromDiet
|
||||||
|
? const EdgeInsets.only(right: 24, left: 4)
|
||||||
|
: null,
|
||||||
title: Text(widget.portion.productName),
|
title: Text(widget.portion.productName),
|
||||||
subtitle: Column(
|
subtitle: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text('${widget.portion.quantity} ${widget.portion.portion == 'gr' ? 'гр' : 'шт'}'),
|
Text(
|
||||||
|
'${widget.portion.quantity} ${widget.portion.portion == 'gr' ? 'гр' : 'шт'}',
|
||||||
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
_buildNutritionalInfo(),
|
_buildNutritionalInfo(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
// ignore: use_full_hex_values_for_flutter_colors
|
// ignore: use_full_hex_values_for_flutter_colors
|
||||||
tileColor: const Color(0xfff6faff03).withValues(alpha: 0.01),
|
tileColor: const Color(0xfff6faff03).withValues(alpha: 0.01),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
Widget? trailing;
|
Widget? trailing;
|
||||||
if (state.isConsultantView && !isDiaryCommented) {
|
if (state.isConsultantView && !state.isDiaryCommented) {
|
||||||
trailing = GestureDetector(
|
trailing = GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -180,13 +220,15 @@ class _ProductWidgetState extends State<ProductWidget> {
|
|||||||
},
|
},
|
||||||
child: SvgPicture.asset('assets/icons/edit.svg'),
|
child: SvgPicture.asset('assets/icons/edit.svg'),
|
||||||
);
|
);
|
||||||
} else if (!isDiaryCommented && !widget.portion.fromDiet) {
|
} else if (canEditPortion) {
|
||||||
trailing = const Icon(Icons.more_vert_rounded);
|
trailing = const Icon(Icons.more_vert_rounded);
|
||||||
}
|
}
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: canEditPortion ? () => updateProductInMeal(state, widget.portion) : null,
|
onTap: canEditPortion
|
||||||
|
? () => updateProductInMeal(state, widget.portion)
|
||||||
|
: null,
|
||||||
trailing: trailing,
|
trailing: trailing,
|
||||||
leading: widget.portion.fromDiet
|
leading: widget.portion.fromDiet
|
||||||
? Transform.scale(
|
? Transform.scale(
|
||||||
@@ -199,35 +241,55 @@ class _ProductWidgetState extends State<ProductWidget> {
|
|||||||
isEaten = value!;
|
isEaten = value!;
|
||||||
});
|
});
|
||||||
if (isEaten) {
|
if (isEaten) {
|
||||||
diaryCubit.markEaten(diaryId: state.diary.id, mealId: state.selectedMeal, portionId: widget.portion.id);
|
diaryCubit.markEaten(
|
||||||
|
diaryId: state.diary.id,
|
||||||
|
mealId: state.selectedMeal,
|
||||||
|
portionId: widget.portion.id,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
diaryCubit.unmarkEaten(diaryId: state.diary.id, mealId: state.selectedMeal, portionId: widget.portion.id);
|
diaryCubit.unmarkEaten(
|
||||||
|
diaryId: state.diary.id,
|
||||||
|
mealId: state.selectedMeal,
|
||||||
|
portionId: widget.portion.id,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
activeColor: appTheme.blueA400,
|
activeColor: appTheme.blueA400,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
),
|
||||||
side: const BorderSide(color: Color(0xFF809CB9)),
|
side: const BorderSide(color: Color(0xFF809CB9)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
horizontalTitleGap: 0,
|
horizontalTitleGap: 0,
|
||||||
contentPadding: widget.portion.fromDiet ? const EdgeInsets.only(right: 24, left: 4) : null,
|
contentPadding: widget.portion.fromDiet
|
||||||
|
? const EdgeInsets.only(right: 24, left: 4)
|
||||||
|
: null,
|
||||||
title: Text(widget.portion.productName),
|
title: Text(widget.portion.productName),
|
||||||
subtitle: Column(
|
subtitle: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text('${widget.portion.quantity} ${widget.portion.portion == 'gr' ? 'гр' : 'шт'}'),
|
Text(
|
||||||
|
'${widget.portion.quantity} ${widget.portion.portion == 'gr' ? 'гр' : 'шт'}',
|
||||||
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
_buildNutritionalInfo(),
|
_buildNutritionalInfo(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
tileColor: const Color(0xFFF6FAFF),
|
tileColor: const Color(0xFFF6FAFF),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
child: PortionCommentWidget(enabled: showCommentWidget, portionId: widget.portion.id, comment: portionComment?.comment),
|
child: PortionCommentWidget(
|
||||||
|
enabled: showCommentWidget,
|
||||||
|
portionId: widget.portion.id,
|
||||||
|
comment: portionComment?.comment,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ class _SatietyRatingWidgetState extends State<SatietyRatingWidget> {
|
|||||||
final meals = state.diary.meals;
|
final meals = state.diary.meals;
|
||||||
if (selectedIndex >= 0 && selectedIndex < meals.length) {
|
if (selectedIndex >= 0 && selectedIndex < meals.length) {
|
||||||
if (isFirstFetch[selectedIndex]) {
|
if (isFirstFetch[selectedIndex]) {
|
||||||
currentSelectedRating[selectedIndex] = meals[selectedIndex].satiety.toDouble() + 1;
|
currentSelectedRating[selectedIndex] =
|
||||||
|
meals[selectedIndex].satiety.toDouble() + 1;
|
||||||
isFirstFetch[selectedIndex] = false;
|
isFirstFetch[selectedIndex] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,21 +37,32 @@ class _SatietyRatingWidgetState extends State<SatietyRatingWidget> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
state.isConsultantView ? 'Сытость пользователя после приема пищи' : 'Ваша сытость после приёма пищи',
|
state.isConsultantView
|
||||||
|
? 'Сытость пользователя после приема пищи'
|
||||||
|
: 'Ваша сытость после приёма пищи',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(fontSize: 16),
|
style: const TextStyle(fontSize: 16),
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
SvgPicture.asset('assets/icons/satiety_emoji_left.svg', height: 32, width: 32),
|
SvgPicture.asset(
|
||||||
|
'assets/icons/satiety_emoji_left.svg',
|
||||||
|
height: 32,
|
||||||
|
width: 32,
|
||||||
|
),
|
||||||
RatingBar.builder(
|
RatingBar.builder(
|
||||||
ignoreGestures: state.isConsultantView || state.isDiaryFinished || state.isDiaryCommented,
|
ignoreGestures: !state.canEditDiary,
|
||||||
initialRating: (selectedIndex >= 0 && selectedIndex < state.diary.meals.length)
|
initialRating:
|
||||||
? state.diary.meals[selectedIndex].satiety.toDouble() + 1
|
(selectedIndex >= 0 &&
|
||||||
|
selectedIndex < state.diary.meals.length)
|
||||||
|
? state.diary.meals[selectedIndex].satiety
|
||||||
|
.toDouble() +
|
||||||
|
1
|
||||||
: 3,
|
: 3,
|
||||||
itemCount: 6,
|
itemCount: 6,
|
||||||
itemSize: MediaQuery.of(context).size.width / 6 - (124 / 6),
|
itemSize:
|
||||||
|
MediaQuery.of(context).size.width / 6 - (124 / 6),
|
||||||
updateOnDrag: true,
|
updateOnDrag: true,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
switch (index) {
|
switch (index) {
|
||||||
@@ -58,82 +70,154 @@ class _SatietyRatingWidgetState extends State<SatietyRatingWidget> {
|
|||||||
return Container(
|
return Container(
|
||||||
width: MediaQuery.of(context).size.width / 6,
|
width: MediaQuery.of(context).size.width / 6,
|
||||||
height: 24,
|
height: 24,
|
||||||
margin: currentSelectedRating[selectedIndex] == 1 ? const EdgeInsets.symmetric(horizontal: 2) : null,
|
margin: currentSelectedRating[selectedIndex] == 1
|
||||||
|
? const EdgeInsets.symmetric(horizontal: 2)
|
||||||
|
: null,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFC82222),
|
color: const Color(0xFFC82222),
|
||||||
border: currentSelectedRating[selectedIndex] == 1
|
border:
|
||||||
? Border.all(width: 2, color: const Color(0xFFAA3333), strokeAlign: BorderSide.strokeAlignOutside)
|
currentSelectedRating[selectedIndex] == 1
|
||||||
|
? Border.all(
|
||||||
|
width: 2,
|
||||||
|
color: const Color(0xFFAA3333),
|
||||||
|
strokeAlign:
|
||||||
|
BorderSide.strokeAlignOutside,
|
||||||
|
)
|
||||||
: null,
|
: null,
|
||||||
borderRadius: currentSelectedRating[selectedIndex] == 1
|
borderRadius:
|
||||||
? const BorderRadius.horizontal(left: Radius.circular(8), right: Radius.circular(2))
|
currentSelectedRating[selectedIndex] == 1
|
||||||
: const BorderRadius.horizontal(left: Radius.circular(8)),
|
? const BorderRadius.horizontal(
|
||||||
|
left: Radius.circular(8),
|
||||||
|
right: Radius.circular(2),
|
||||||
|
)
|
||||||
|
: const BorderRadius.horizontal(
|
||||||
|
left: Radius.circular(8),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
case 1:
|
case 1:
|
||||||
return Container(
|
return Container(
|
||||||
width: MediaQuery.of(context).size.width / 6,
|
width: MediaQuery.of(context).size.width / 6,
|
||||||
height: 24,
|
height: 24,
|
||||||
margin: currentSelectedRating[selectedIndex] == 2 ? const EdgeInsets.symmetric(horizontal: 2) : null,
|
margin: currentSelectedRating[selectedIndex] == 2
|
||||||
|
? const EdgeInsets.symmetric(horizontal: 2)
|
||||||
|
: null,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFFF3E3E),
|
color: const Color(0xFFFF3E3E),
|
||||||
border: currentSelectedRating[selectedIndex] == 2
|
border:
|
||||||
? Border.all(width: 2, color: const Color(0xFF550000), strokeAlign: BorderSide.strokeAlignOutside)
|
currentSelectedRating[selectedIndex] == 2
|
||||||
|
? Border.all(
|
||||||
|
width: 2,
|
||||||
|
color: const Color(0xFF550000),
|
||||||
|
strokeAlign:
|
||||||
|
BorderSide.strokeAlignOutside,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
borderRadius:
|
||||||
|
currentSelectedRating[selectedIndex] == 2
|
||||||
|
? BorderRadius.circular(2)
|
||||||
: null,
|
: null,
|
||||||
borderRadius: currentSelectedRating[selectedIndex] == 2 ? BorderRadius.circular(2) : null,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
case 2:
|
case 2:
|
||||||
return Container(
|
return Container(
|
||||||
width: MediaQuery.of(context).size.width / 6,
|
width: MediaQuery.of(context).size.width / 6,
|
||||||
height: 24,
|
height: 24,
|
||||||
margin: currentSelectedRating[selectedIndex] == 3 ? const EdgeInsets.symmetric(horizontal: 2) : null,
|
margin: currentSelectedRating[selectedIndex] == 3
|
||||||
|
? const EdgeInsets.symmetric(horizontal: 2)
|
||||||
|
: null,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFF8C926),
|
color: const Color(0xFFF8C926),
|
||||||
border: currentSelectedRating[selectedIndex] == 3
|
border:
|
||||||
? Border.all(width: 2, color: const Color(0xFF8C5D00), strokeAlign: BorderSide.strokeAlignOutside)
|
currentSelectedRating[selectedIndex] == 3
|
||||||
|
? Border.all(
|
||||||
|
width: 2,
|
||||||
|
color: const Color(0xFF8C5D00),
|
||||||
|
strokeAlign:
|
||||||
|
BorderSide.strokeAlignOutside,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
borderRadius:
|
||||||
|
currentSelectedRating[selectedIndex] == 3
|
||||||
|
? BorderRadius.circular(2)
|
||||||
: null,
|
: null,
|
||||||
borderRadius: currentSelectedRating[selectedIndex] == 3 ? BorderRadius.circular(2) : null,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
case 3:
|
case 3:
|
||||||
return Container(
|
return Container(
|
||||||
width: MediaQuery.of(context).size.width / 6,
|
width: MediaQuery.of(context).size.width / 6,
|
||||||
height: 24,
|
height: 24,
|
||||||
margin: currentSelectedRating[selectedIndex] == 4 ? const EdgeInsets.symmetric(horizontal: 2) : null,
|
margin: currentSelectedRating[selectedIndex] == 4
|
||||||
|
? const EdgeInsets.symmetric(horizontal: 2)
|
||||||
|
: null,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFFFF3BF),
|
color: const Color(0xFFFFF3BF),
|
||||||
border: currentSelectedRating[selectedIndex] == 4
|
border:
|
||||||
? Border.all(width: 2, color: const Color(0xFFB5911B), strokeAlign: BorderSide.strokeAlignOutside)
|
currentSelectedRating[selectedIndex] == 4
|
||||||
|
? Border.all(
|
||||||
|
width: 2,
|
||||||
|
color: const Color(0xFFB5911B),
|
||||||
|
strokeAlign:
|
||||||
|
BorderSide.strokeAlignOutside,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
borderRadius:
|
||||||
|
currentSelectedRating[selectedIndex] == 4
|
||||||
|
? BorderRadius.circular(2)
|
||||||
: null,
|
: null,
|
||||||
borderRadius: currentSelectedRating[selectedIndex] == 4 ? BorderRadius.circular(2) : null,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
case 4:
|
case 4:
|
||||||
return Container(
|
return Container(
|
||||||
width: MediaQuery.of(context).size.width / 6,
|
width: MediaQuery.of(context).size.width / 6,
|
||||||
height: 24,
|
height: 24,
|
||||||
margin: currentSelectedRating[selectedIndex] == 5 ? const EdgeInsets.symmetric(horizontal: 2) : null,
|
margin: currentSelectedRating[selectedIndex] == 5
|
||||||
|
? const EdgeInsets.symmetric(horizontal: 2)
|
||||||
|
: null,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFF62E833),
|
color: const Color(0xFF62E833),
|
||||||
border: currentSelectedRating[selectedIndex] == 5
|
border:
|
||||||
? Border.all(width: 2, color: const Color(0xFF276918), strokeAlign: BorderSide.strokeAlignOutside)
|
currentSelectedRating[selectedIndex] == 5
|
||||||
|
? Border.all(
|
||||||
|
width: 2,
|
||||||
|
color: const Color(0xFF276918),
|
||||||
|
strokeAlign:
|
||||||
|
BorderSide.strokeAlignOutside,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
borderRadius:
|
||||||
|
currentSelectedRating[selectedIndex] == 5
|
||||||
|
? BorderRadius.circular(2)
|
||||||
: null,
|
: null,
|
||||||
borderRadius: currentSelectedRating[selectedIndex] == 5 ? BorderRadius.circular(2) : null,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return Container(
|
return Container(
|
||||||
width: MediaQuery.of(context).size.width / 6,
|
width: MediaQuery.of(context).size.width / 6,
|
||||||
height: 24,
|
height: 24,
|
||||||
margin: currentSelectedRating[selectedIndex] == 6 ? const EdgeInsets.symmetric(horizontal: 2) : null,
|
margin: currentSelectedRating[selectedIndex] == 6
|
||||||
|
? const EdgeInsets.symmetric(horizontal: 2)
|
||||||
|
: null,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFF4DCE20),
|
color: const Color(0xFF4DCE20),
|
||||||
border: currentSelectedRating[selectedIndex] == 6
|
border:
|
||||||
? Border.all(width: 2, color: const Color(0xFF184D12), strokeAlign: BorderSide.strokeAlignOutside)
|
currentSelectedRating[selectedIndex] == 6
|
||||||
|
? Border.all(
|
||||||
|
width: 2,
|
||||||
|
color: const Color(0xFF184D12),
|
||||||
|
strokeAlign:
|
||||||
|
BorderSide.strokeAlignOutside,
|
||||||
|
)
|
||||||
: null,
|
: null,
|
||||||
borderRadius: currentSelectedRating[selectedIndex] == 6
|
borderRadius:
|
||||||
? const BorderRadius.horizontal(left: Radius.circular(2), right: Radius.circular(8))
|
currentSelectedRating[selectedIndex] == 6
|
||||||
: const BorderRadius.horizontal(right: Radius.circular(8)),
|
? const BorderRadius.horizontal(
|
||||||
|
left: Radius.circular(2),
|
||||||
|
right: Radius.circular(8),
|
||||||
|
)
|
||||||
|
: const BorderRadius.horizontal(
|
||||||
|
right: Radius.circular(8),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -143,12 +227,25 @@ class _SatietyRatingWidgetState extends State<SatietyRatingWidget> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
currentSelectedRating[selectedIndex] = value;
|
currentSelectedRating[selectedIndex] = value;
|
||||||
});
|
});
|
||||||
EasyDebounce.debounce('change-satiety', const Duration(milliseconds: 250), () {
|
EasyDebounce.debounce(
|
||||||
context.read<DiaryCubit>().fillSatiety(diaryId: state.diary.id, satiety: currentSelectedRating[selectedIndex].toInt() - 1);
|
'change-satiety',
|
||||||
});
|
const Duration(milliseconds: 250),
|
||||||
|
() {
|
||||||
|
context.read<DiaryCubit>().fillSatiety(
|
||||||
|
diaryId: state.diary.id,
|
||||||
|
satiety:
|
||||||
|
currentSelectedRating[selectedIndex].toInt() -
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SvgPicture.asset('assets/icons/satiety_emoji_right.svg', height: 32, width: 32),
|
SvgPicture.asset(
|
||||||
|
'assets/icons/satiety_emoji_right.svg',
|
||||||
|
height: 32,
|
||||||
|
width: 32,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -46,7 +46,8 @@ class _TimeOfMealPickerWidgetState extends State<TimeOfMealPickerWidget> {
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
diaryCubit.fillTime(
|
diaryCubit.fillTime(
|
||||||
diaryId: diaryId,
|
diaryId: diaryId,
|
||||||
time: '${value.hour.toString().padLeft(2, '0')}:${value.minute.toString().padLeft(2, '0')}:00',
|
time:
|
||||||
|
'${value.hour.toString().padLeft(2, '0')}:${value.minute.toString().padLeft(2, '0')}:00',
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
@@ -69,7 +70,7 @@ class _TimeOfMealPickerWidgetState extends State<TimeOfMealPickerWidget> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 30)
|
const SizedBox(height: 30),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -98,7 +99,9 @@ class _TimeOfMealPickerWidgetState extends State<TimeOfMealPickerWidget> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: state.isConsultantView || state.isDiaryFinished || state.isDiaryCommented ? null : () => showTimePicker(state.diary.id),
|
onTap: state.canEditDiary
|
||||||
|
? () => showTimePicker(state.diary.id)
|
||||||
|
: null,
|
||||||
borderRadius: BorderRadius.circular(100),
|
borderRadius: BorderRadius.circular(100),
|
||||||
child: Ink(
|
child: Ink(
|
||||||
height: 32,
|
height: 32,
|
||||||
@@ -110,7 +113,16 @@ class _TimeOfMealPickerWidgetState extends State<TimeOfMealPickerWidget> {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: Text(
|
child: Text(
|
||||||
state.diary.meals[state.selectedMeal - 1].time.substring(0, state.diary.meals[state.selectedMeal - 1].time.length - 3),
|
state.diary.meals[state.selectedMeal - 1].time
|
||||||
|
.substring(
|
||||||
|
0,
|
||||||
|
state
|
||||||
|
.diary
|
||||||
|
.meals[state.selectedMeal - 1]
|
||||||
|
.time
|
||||||
|
.length -
|
||||||
|
3,
|
||||||
|
),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Color(0xFF6D6F71),
|
color: Color(0xFF6D6F71),
|
||||||
fontSize: 17,
|
fontSize: 17,
|
||||||
|
|||||||
59
test/features/diary/presentation/diary_edit_access_test.dart
Normal file
59
test/features/diary/presentation/diary_edit_access_test.dart
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import 'package:eatvio_app/features/diary/presentation/diary_edit_access.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
final now = DateTime(2026, 9, 30, 12);
|
||||||
|
|
||||||
|
bool canEdit({
|
||||||
|
bool isConsultantView = false,
|
||||||
|
bool isDiaryCommented = false,
|
||||||
|
bool isDiaryFinished = false,
|
||||||
|
int closed = 0,
|
||||||
|
String diaryDate = '2026-09-28',
|
||||||
|
}) {
|
||||||
|
return canEditDiaryDay(
|
||||||
|
isConsultantView: isConsultantView,
|
||||||
|
isDiaryCommented: isDiaryCommented,
|
||||||
|
isDiaryFinished: isDiaryFinished,
|
||||||
|
closed: closed,
|
||||||
|
diaryDate: diaryDate,
|
||||||
|
now: now,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
test('finished today and yesterday stay editable', () {
|
||||||
|
expect(canEdit(isDiaryFinished: true, diaryDate: '2026-09-30'), isTrue);
|
||||||
|
expect(canEdit(isDiaryFinished: true, diaryDate: '2026-09-29'), isTrue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('finished days older than yesterday stay read-only', () {
|
||||||
|
expect(canEdit(isDiaryFinished: true, diaryDate: '2026-09-28'), isFalse);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('commented days stay read-only inside the window', () {
|
||||||
|
expect(canEdit(isDiaryCommented: true, diaryDate: '2026-09-30'), isFalse);
|
||||||
|
expect(
|
||||||
|
canEdit(
|
||||||
|
isDiaryCommented: true,
|
||||||
|
isDiaryFinished: true,
|
||||||
|
diaryDate: '2026-09-29',
|
||||||
|
),
|
||||||
|
isFalse,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('an open day outside the window stays editable', () {
|
||||||
|
expect(canEdit(diaryDate: '2026-09-01'), isTrue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a closed flag blocks editing even inside the window', () {
|
||||||
|
expect(
|
||||||
|
canEdit(isDiaryFinished: true, closed: 1, diaryDate: '2026-09-30'),
|
||||||
|
isFalse,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('consultant view stays read-only', () {
|
||||||
|
expect(canEdit(isConsultantView: true, diaryDate: '2026-09-30'), isFalse);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -25,16 +25,25 @@ import 'package:mockito/mockito.dart';
|
|||||||
|
|
||||||
import 'diary_screen_smoke_test.mocks.dart';
|
import 'diary_screen_smoke_test.mocks.dart';
|
||||||
|
|
||||||
@GenerateNiceMocks([MockSpec<DiaryCubit>(), MockSpec<ConsultantCommentsCubit>()])
|
@GenerateNiceMocks([
|
||||||
|
MockSpec<DiaryCubit>(),
|
||||||
|
MockSpec<ConsultantCommentsCubit>(),
|
||||||
|
])
|
||||||
void main() {
|
void main() {
|
||||||
group('DiaryScreen smoke', () {
|
group('DiaryScreen smoke', () {
|
||||||
setUpAll(() async {
|
setUpAll(() async {
|
||||||
dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost');
|
dotenv.loadFromString(
|
||||||
|
envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost',
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
await sl.reset();
|
await sl.reset();
|
||||||
sl.registerLazySingleton<AppConfig>(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost'));
|
sl.registerLazySingleton<AppConfig>(
|
||||||
|
() =>
|
||||||
|
AppConfig(buildType: AppBuildType.tests)
|
||||||
|
..setBaseUrl('http://localhost'),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
Widget wrap(Widget child) {
|
Widget wrap(Widget child) {
|
||||||
@@ -51,23 +60,53 @@ void main() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
DiaryStateLoaded loadedState({required bool hasChosenPlan}) {
|
DiaryStateLoaded loadedState({
|
||||||
|
required bool hasChosenPlan,
|
||||||
|
String date = '2025-01-01',
|
||||||
|
bool isDiaryFinished = false,
|
||||||
|
bool isDiaryCommented = false,
|
||||||
|
int closed = 0,
|
||||||
|
}) {
|
||||||
final diary = DiaryDayEntity(
|
final diary = DiaryDayEntity(
|
||||||
id: 1,
|
id: 1,
|
||||||
date: '2025-01-01',
|
date: date,
|
||||||
wakeup: '07:00',
|
wakeup: '07:00',
|
||||||
retire: '23:00',
|
retire: '23:00',
|
||||||
closed: 0,
|
closed: closed,
|
||||||
comment: '',
|
comment: '',
|
||||||
typeOfDiary: TypeOfDiaryModel(hasChosenDiet: false, hasChosenPlan: hasChosenPlan, diaryState: null),
|
typeOfDiary: TypeOfDiaryModel(
|
||||||
meals: const [MealModel(id: 1, time: '08:00', typeNumber: 1, satiety: 0, mood: 0, portions: [], photos: [])],
|
hasChosenDiet: false,
|
||||||
|
hasChosenPlan: hasChosenPlan,
|
||||||
|
diaryState: null,
|
||||||
|
),
|
||||||
|
meals: const [
|
||||||
|
MealModel(
|
||||||
|
id: 1,
|
||||||
|
time: '08:00',
|
||||||
|
typeNumber: 1,
|
||||||
|
satiety: 0,
|
||||||
|
mood: 0,
|
||||||
|
portions: [],
|
||||||
|
photos: [],
|
||||||
|
),
|
||||||
|
],
|
||||||
chosenPlanId: hasChosenPlan ? 10 : null,
|
chosenPlanId: hasChosenPlan ? 10 : null,
|
||||||
dayOfChosenDiet: null,
|
dayOfChosenDiet: null,
|
||||||
diet: null,
|
diet: null,
|
||||||
);
|
);
|
||||||
const nutrition = NutritionDataModel(
|
const nutrition = NutritionDataModel(
|
||||||
dailyNutritionIntake: NutritionModel(energy: 0, proteins: 0, fats: 0, carbohydrates: 0),
|
dailyNutritionIntake: NutritionModel(
|
||||||
dailyNutritionRecommendation: NutritionModel(energy: 0, proteins: 0, fats: 0, carbohydrates: 0),
|
energy: 0,
|
||||||
|
proteins: 0,
|
||||||
|
fats: 0,
|
||||||
|
carbohydrates: 0,
|
||||||
|
),
|
||||||
|
dailyNutritionRecommendation: NutritionModel(
|
||||||
|
energy: 0,
|
||||||
|
proteins: 0,
|
||||||
|
fats: 0,
|
||||||
|
carbohydrates: 0,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
return DiaryState.loaded(
|
return DiaryState.loaded(
|
||||||
diary: diary,
|
diary: diary,
|
||||||
@@ -75,8 +114,8 @@ void main() {
|
|||||||
selectedDay: DateTime(2025, 1, 1),
|
selectedDay: DateTime(2025, 1, 1),
|
||||||
selectedMeal: 1,
|
selectedMeal: 1,
|
||||||
isConsultantView: false,
|
isConsultantView: false,
|
||||||
isDiaryFinished: false,
|
isDiaryFinished: isDiaryFinished,
|
||||||
isDiaryCommented: false,
|
isDiaryCommented: isDiaryCommented,
|
||||||
isWakeUpAndRetireEnabled: true,
|
isWakeUpAndRetireEnabled: true,
|
||||||
isMoodEnabled: true,
|
isMoodEnabled: true,
|
||||||
)
|
)
|
||||||
@@ -86,13 +125,26 @@ void main() {
|
|||||||
testWidgets('renders loading state', (tester) async {
|
testWidgets('renders loading state', (tester) async {
|
||||||
final diaryCubit = MockDiaryCubit();
|
final diaryCubit = MockDiaryCubit();
|
||||||
when(diaryCubit.state).thenReturn(const DiaryState.loading());
|
when(diaryCubit.state).thenReturn(const DiaryState.loading());
|
||||||
when(diaryCubit.stream).thenAnswer((_) => const Stream<DiaryState>.empty());
|
when(
|
||||||
|
diaryCubit.stream,
|
||||||
|
).thenAnswer((_) => const Stream<DiaryState>.empty());
|
||||||
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
|
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||||
when(diaryCubit.fetchDiaryV2(needLoading: anyNamed('needLoading'), diaryId: anyNamed('diaryId'))).thenAnswer((_) async => true);
|
when(
|
||||||
|
diaryCubit.fetchDiaryV2(
|
||||||
|
needLoading: anyNamed('needLoading'),
|
||||||
|
diaryId: anyNamed('diaryId'),
|
||||||
|
),
|
||||||
|
).thenAnswer((_) async => true);
|
||||||
|
|
||||||
final commentsCubit = MockConsultantCommentsCubit();
|
final commentsCubit = MockConsultantCommentsCubit();
|
||||||
when(commentsCubit.state).thenReturn(ConsultantCommentsState(commentsModel: ConsultantCommentModel(comment: '')));
|
when(commentsCubit.state).thenReturn(
|
||||||
when(commentsCubit.stream).thenAnswer((_) => const Stream<ConsultantCommentsState>.empty());
|
ConsultantCommentsState(
|
||||||
|
commentsModel: ConsultantCommentModel(comment: ''),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
when(
|
||||||
|
commentsCubit.stream,
|
||||||
|
).thenAnswer((_) => const Stream<ConsultantCommentsState>.empty());
|
||||||
when(commentsCubit.close()).thenAnswer((_) async => Future<void>.value());
|
when(commentsCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||||
|
|
||||||
sl.registerFactory<DiaryCubit>(() => diaryCubit);
|
sl.registerFactory<DiaryCubit>(() => diaryCubit);
|
||||||
@@ -107,13 +159,27 @@ void main() {
|
|||||||
testWidgets('renders loaded state (chosen plan)', (tester) async {
|
testWidgets('renders loaded state (chosen plan)', (tester) async {
|
||||||
final diaryCubit = MockDiaryCubit();
|
final diaryCubit = MockDiaryCubit();
|
||||||
when(diaryCubit.state).thenReturn(loadedState(hasChosenPlan: true));
|
when(diaryCubit.state).thenReturn(loadedState(hasChosenPlan: true));
|
||||||
when(diaryCubit.stream).thenAnswer((_) => const Stream<DiaryState>.empty());
|
when(
|
||||||
|
diaryCubit.stream,
|
||||||
|
).thenAnswer((_) => const Stream<DiaryState>.empty());
|
||||||
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
|
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||||
when(diaryCubit.fetchDiaryV2(needLoading: anyNamed('needLoading'), diaryId: anyNamed('diaryId'))).thenAnswer((_) async => true);
|
when(
|
||||||
|
diaryCubit.fetchDiaryV2(
|
||||||
|
needLoading: anyNamed('needLoading'),
|
||||||
|
diaryId: anyNamed('diaryId'),
|
||||||
|
),
|
||||||
|
).thenAnswer((_) async => true);
|
||||||
|
|
||||||
final commentsCubit = MockConsultantCommentsCubit();
|
final commentsCubit = MockConsultantCommentsCubit();
|
||||||
when(commentsCubit.state).thenReturn(ConsultantCommentsState(commentsModel: ConsultantCommentModel(comment: ''), dayCommentNeedsToBeFilled: false));
|
when(commentsCubit.state).thenReturn(
|
||||||
when(commentsCubit.stream).thenAnswer((_) => const Stream<ConsultantCommentsState>.empty());
|
ConsultantCommentsState(
|
||||||
|
commentsModel: ConsultantCommentModel(comment: ''),
|
||||||
|
dayCommentNeedsToBeFilled: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
when(
|
||||||
|
commentsCubit.stream,
|
||||||
|
).thenAnswer((_) => const Stream<ConsultantCommentsState>.empty());
|
||||||
when(commentsCubit.close()).thenAnswer((_) async => Future<void>.value());
|
when(commentsCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||||
|
|
||||||
sl.registerFactory<DiaryCubit>(() => diaryCubit);
|
sl.registerFactory<DiaryCubit>(() => diaryCubit);
|
||||||
@@ -124,19 +190,37 @@ void main() {
|
|||||||
|
|
||||||
expect(find.byType(DiaryScreen), findsOneWidget);
|
expect(find.byType(DiaryScreen), findsOneWidget);
|
||||||
|
|
||||||
expect(find.text('Дополнительная информация для консультанта'), findsOneWidget);
|
expect(
|
||||||
|
find.text('Дополнительная информация для консультанта'),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('renders error state and reload button', (tester) async {
|
testWidgets('renders error state and reload button', (tester) async {
|
||||||
final diaryCubit = MockDiaryCubit();
|
final diaryCubit = MockDiaryCubit();
|
||||||
when(diaryCubit.state).thenReturn(const DiaryState.error(errorMessage: 'err'));
|
when(
|
||||||
when(diaryCubit.stream).thenAnswer((_) => const Stream<DiaryState>.empty());
|
diaryCubit.state,
|
||||||
|
).thenReturn(const DiaryState.error(errorMessage: 'err'));
|
||||||
|
when(
|
||||||
|
diaryCubit.stream,
|
||||||
|
).thenAnswer((_) => const Stream<DiaryState>.empty());
|
||||||
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
|
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||||
when(diaryCubit.fetchDiaryV2(needLoading: anyNamed('needLoading'), diaryId: anyNamed('diaryId'))).thenAnswer((_) async => true);
|
when(
|
||||||
|
diaryCubit.fetchDiaryV2(
|
||||||
|
needLoading: anyNamed('needLoading'),
|
||||||
|
diaryId: anyNamed('diaryId'),
|
||||||
|
),
|
||||||
|
).thenAnswer((_) async => true);
|
||||||
|
|
||||||
final commentsCubit = MockConsultantCommentsCubit();
|
final commentsCubit = MockConsultantCommentsCubit();
|
||||||
when(commentsCubit.state).thenReturn(ConsultantCommentsState(commentsModel: ConsultantCommentModel(comment: '')));
|
when(commentsCubit.state).thenReturn(
|
||||||
when(commentsCubit.stream).thenAnswer((_) => const Stream<ConsultantCommentsState>.empty());
|
ConsultantCommentsState(
|
||||||
|
commentsModel: ConsultantCommentModel(comment: ''),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
when(
|
||||||
|
commentsCubit.stream,
|
||||||
|
).thenAnswer((_) => const Stream<ConsultantCommentsState>.empty());
|
||||||
when(commentsCubit.close()).thenAnswer((_) async => Future<void>.value());
|
when(commentsCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||||
|
|
||||||
sl.registerFactory<DiaryCubit>(() => diaryCubit);
|
sl.registerFactory<DiaryCubit>(() => diaryCubit);
|
||||||
@@ -153,19 +237,52 @@ void main() {
|
|||||||
final diaryCubit = MockDiaryCubit();
|
final diaryCubit = MockDiaryCubit();
|
||||||
const futureDiary = DiaryFutureDietDayEntity(
|
const futureDiary = DiaryFutureDietDayEntity(
|
||||||
date: '2025-01-02',
|
date: '2025-01-02',
|
||||||
typeOfDiary: TypeOfDiaryModel(hasChosenDiet: true, hasChosenPlan: false, diaryState: null),
|
typeOfDiary: TypeOfDiaryModel(
|
||||||
meals: [MealModel(id: 1, time: '08:00', typeNumber: 1, satiety: 0, mood: 0, portions: [], photos: [])],
|
hasChosenDiet: true,
|
||||||
|
hasChosenPlan: false,
|
||||||
|
diaryState: null,
|
||||||
|
),
|
||||||
|
meals: [
|
||||||
|
MealModel(
|
||||||
|
id: 1,
|
||||||
|
time: '08:00',
|
||||||
|
typeNumber: 1,
|
||||||
|
satiety: 0,
|
||||||
|
mood: 0,
|
||||||
|
portions: [],
|
||||||
|
photos: [],
|
||||||
|
),
|
||||||
|
],
|
||||||
diet: null,
|
diet: null,
|
||||||
dayOfChosenDiet: 1,
|
dayOfChosenDiet: 1,
|
||||||
);
|
);
|
||||||
when(diaryCubit.state).thenReturn(DiaryState.loadedTomorrowWithDiet(diary: futureDiary, selectedDay: DateTime(2025, 1, 2), selectedMeal: 1));
|
when(diaryCubit.state).thenReturn(
|
||||||
when(diaryCubit.stream).thenAnswer((_) => const Stream<DiaryState>.empty());
|
DiaryState.loadedTomorrowWithDiet(
|
||||||
|
diary: futureDiary,
|
||||||
|
selectedDay: DateTime(2025, 1, 2),
|
||||||
|
selectedMeal: 1,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
when(
|
||||||
|
diaryCubit.stream,
|
||||||
|
).thenAnswer((_) => const Stream<DiaryState>.empty());
|
||||||
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
|
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||||
when(diaryCubit.fetchDiaryV2(needLoading: anyNamed('needLoading'), diaryId: anyNamed('diaryId'))).thenAnswer((_) async => true);
|
when(
|
||||||
|
diaryCubit.fetchDiaryV2(
|
||||||
|
needLoading: anyNamed('needLoading'),
|
||||||
|
diaryId: anyNamed('diaryId'),
|
||||||
|
),
|
||||||
|
).thenAnswer((_) async => true);
|
||||||
|
|
||||||
final commentsCubit = MockConsultantCommentsCubit();
|
final commentsCubit = MockConsultantCommentsCubit();
|
||||||
when(commentsCubit.state).thenReturn(ConsultantCommentsState(commentsModel: ConsultantCommentModel(comment: '')));
|
when(commentsCubit.state).thenReturn(
|
||||||
when(commentsCubit.stream).thenAnswer((_) => const Stream<ConsultantCommentsState>.empty());
|
ConsultantCommentsState(
|
||||||
|
commentsModel: ConsultantCommentModel(comment: ''),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
when(
|
||||||
|
commentsCubit.stream,
|
||||||
|
).thenAnswer((_) => const Stream<ConsultantCommentsState>.empty());
|
||||||
when(commentsCubit.close()).thenAnswer((_) async => Future<void>.value());
|
when(commentsCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||||
|
|
||||||
sl.registerFactory<DiaryCubit>(() => diaryCubit);
|
sl.registerFactory<DiaryCubit>(() => diaryCubit);
|
||||||
@@ -187,17 +304,35 @@ void main() {
|
|||||||
planName: 'Plan',
|
planName: 'Plan',
|
||||||
planDuration: 30,
|
planDuration: 30,
|
||||||
currentPlanDay: 1,
|
currentPlanDay: 1,
|
||||||
consultantComments: ConsultantComments(dayComment: 'Nice', mealsComments: const [], portionsComments: const []),
|
consultantComments: ConsultantComments(
|
||||||
|
dayComment: 'Nice',
|
||||||
|
mealsComments: const [],
|
||||||
|
portionsComments: const [],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
when(diaryCubit.state).thenReturn(state);
|
when(diaryCubit.state).thenReturn(state);
|
||||||
when(diaryCubit.stream).thenAnswer((_) => const Stream<DiaryState>.empty());
|
when(
|
||||||
|
diaryCubit.stream,
|
||||||
|
).thenAnswer((_) => const Stream<DiaryState>.empty());
|
||||||
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
|
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||||
when(diaryCubit.fetchDiaryV2(needLoading: anyNamed('needLoading'), diaryId: anyNamed('diaryId'))).thenAnswer((_) async => true);
|
when(
|
||||||
|
diaryCubit.fetchDiaryV2(
|
||||||
|
needLoading: anyNamed('needLoading'),
|
||||||
|
diaryId: anyNamed('diaryId'),
|
||||||
|
),
|
||||||
|
).thenAnswer((_) async => true);
|
||||||
|
|
||||||
final commentsCubit = MockConsultantCommentsCubit();
|
final commentsCubit = MockConsultantCommentsCubit();
|
||||||
when(commentsCubit.state).thenReturn(ConsultantCommentsState(commentsModel: ConsultantCommentModel(comment: ''), dayCommentNeedsToBeFilled: false));
|
when(commentsCubit.state).thenReturn(
|
||||||
when(commentsCubit.stream).thenAnswer((_) => const Stream<ConsultantCommentsState>.empty());
|
ConsultantCommentsState(
|
||||||
|
commentsModel: ConsultantCommentModel(comment: ''),
|
||||||
|
dayCommentNeedsToBeFilled: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
when(
|
||||||
|
commentsCubit.stream,
|
||||||
|
).thenAnswer((_) => const Stream<ConsultantCommentsState>.empty());
|
||||||
when(commentsCubit.close()).thenAnswer((_) async => Future<void>.value());
|
when(commentsCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||||
when(commentsCubit.submitComments(any)).thenAnswer((_) async => true);
|
when(commentsCubit.submitComments(any)).thenAnswer((_) async => true);
|
||||||
|
|
||||||
@@ -215,13 +350,26 @@ void main() {
|
|||||||
final diaryCubit = MockDiaryCubit();
|
final diaryCubit = MockDiaryCubit();
|
||||||
final state = loadedState(hasChosenPlan: false);
|
final state = loadedState(hasChosenPlan: false);
|
||||||
when(diaryCubit.state).thenReturn(state);
|
when(diaryCubit.state).thenReturn(state);
|
||||||
when(diaryCubit.stream).thenAnswer((_) => const Stream<DiaryState>.empty());
|
when(
|
||||||
|
diaryCubit.stream,
|
||||||
|
).thenAnswer((_) => const Stream<DiaryState>.empty());
|
||||||
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
|
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||||
when(diaryCubit.fetchDiaryV2(needLoading: anyNamed('needLoading'), diaryId: anyNamed('diaryId'))).thenAnswer((_) async => true);
|
when(
|
||||||
|
diaryCubit.fetchDiaryV2(
|
||||||
|
needLoading: anyNamed('needLoading'),
|
||||||
|
diaryId: anyNamed('diaryId'),
|
||||||
|
),
|
||||||
|
).thenAnswer((_) async => true);
|
||||||
|
|
||||||
final commentsCubit = MockConsultantCommentsCubit();
|
final commentsCubit = MockConsultantCommentsCubit();
|
||||||
when(commentsCubit.state).thenReturn(ConsultantCommentsState(commentsModel: ConsultantCommentModel(comment: '')));
|
when(commentsCubit.state).thenReturn(
|
||||||
when(commentsCubit.stream).thenAnswer((_) => const Stream<ConsultantCommentsState>.empty());
|
ConsultantCommentsState(
|
||||||
|
commentsModel: ConsultantCommentModel(comment: ''),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
when(
|
||||||
|
commentsCubit.stream,
|
||||||
|
).thenAnswer((_) => const Stream<ConsultantCommentsState>.empty());
|
||||||
when(commentsCubit.close()).thenAnswer((_) async => Future<void>.value());
|
when(commentsCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||||
|
|
||||||
sl.registerFactory<DiaryCubit>(() => diaryCubit);
|
sl.registerFactory<DiaryCubit>(() => diaryCubit);
|
||||||
@@ -234,5 +382,96 @@ void main() {
|
|||||||
|
|
||||||
expect(find.byType(Text), findsWidgets);
|
expect(find.byType(Text), findsWidgets);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('shows add button for a finished day inside the edit window', (
|
||||||
|
tester,
|
||||||
|
) async {
|
||||||
|
final yesterday = DateTime.now().subtract(const Duration(days: 1));
|
||||||
|
final diaryCubit = MockDiaryCubit();
|
||||||
|
when(diaryCubit.state).thenReturn(
|
||||||
|
loadedState(
|
||||||
|
hasChosenPlan: false,
|
||||||
|
date: _calendarDate(yesterday),
|
||||||
|
isDiaryFinished: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
when(
|
||||||
|
diaryCubit.stream,
|
||||||
|
).thenAnswer((_) => const Stream<DiaryState>.empty());
|
||||||
|
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||||
|
when(
|
||||||
|
diaryCubit.fetchDiaryV2(
|
||||||
|
needLoading: anyNamed('needLoading'),
|
||||||
|
diaryId: anyNamed('diaryId'),
|
||||||
|
),
|
||||||
|
).thenAnswer((_) async => true);
|
||||||
|
|
||||||
|
final commentsCubit = MockConsultantCommentsCubit();
|
||||||
|
when(commentsCubit.state).thenReturn(
|
||||||
|
ConsultantCommentsState(
|
||||||
|
commentsModel: ConsultantCommentModel(comment: ''),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
when(
|
||||||
|
commentsCubit.stream,
|
||||||
|
).thenAnswer((_) => const Stream<ConsultantCommentsState>.empty());
|
||||||
|
when(commentsCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||||
|
|
||||||
|
sl.registerFactory<DiaryCubit>(() => diaryCubit);
|
||||||
|
sl.registerFactory<ConsultantCommentsCubit>(() => commentsCubit);
|
||||||
|
|
||||||
|
await tester.pumpWidget(wrap(DiaryScreen(date: yesterday)));
|
||||||
|
await tester.pump(const Duration(milliseconds: 200));
|
||||||
|
|
||||||
|
expect(find.text('Добавить'), findsOneWidget);
|
||||||
|
expect(find.text('Закончить день'), findsNothing);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('hides add button for a commented day', (tester) async {
|
||||||
|
final yesterday = DateTime.now().subtract(const Duration(days: 1));
|
||||||
|
final diaryCubit = MockDiaryCubit();
|
||||||
|
when(diaryCubit.state).thenReturn(
|
||||||
|
loadedState(
|
||||||
|
hasChosenPlan: false,
|
||||||
|
date: _calendarDate(yesterday),
|
||||||
|
isDiaryCommented: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
when(
|
||||||
|
diaryCubit.stream,
|
||||||
|
).thenAnswer((_) => const Stream<DiaryState>.empty());
|
||||||
|
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||||
|
when(
|
||||||
|
diaryCubit.fetchDiaryV2(
|
||||||
|
needLoading: anyNamed('needLoading'),
|
||||||
|
diaryId: anyNamed('diaryId'),
|
||||||
|
),
|
||||||
|
).thenAnswer((_) async => true);
|
||||||
|
|
||||||
|
final commentsCubit = MockConsultantCommentsCubit();
|
||||||
|
when(commentsCubit.state).thenReturn(
|
||||||
|
ConsultantCommentsState(
|
||||||
|
commentsModel: ConsultantCommentModel(comment: ''),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
when(
|
||||||
|
commentsCubit.stream,
|
||||||
|
).thenAnswer((_) => const Stream<ConsultantCommentsState>.empty());
|
||||||
|
when(commentsCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||||
|
|
||||||
|
sl.registerFactory<DiaryCubit>(() => diaryCubit);
|
||||||
|
sl.registerFactory<ConsultantCommentsCubit>(() => commentsCubit);
|
||||||
|
|
||||||
|
await tester.pumpWidget(wrap(DiaryScreen(date: yesterday)));
|
||||||
|
await tester.pump(const Duration(milliseconds: 200));
|
||||||
|
|
||||||
|
expect(find.text('Добавить'), findsNothing);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _calendarDate(DateTime date) {
|
||||||
|
final month = date.month.toString().padLeft(2, '0');
|
||||||
|
final day = date.day.toString().padLeft(2, '0');
|
||||||
|
return '${date.year}-$month-$day';
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import 'package:eatvio_app/core/theme/theme_helper.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/consultations/models/consultation_model.dart';
|
||||||
import 'package:eatvio_app/features/diary/entities/diary_day_entity.dart';
|
import 'package:eatvio_app/features/diary/entities/diary_day_entity.dart';
|
||||||
@@ -27,7 +26,8 @@ class FakeDiaryCubit extends Cubit<DiaryState> implements DiaryCubit {
|
|||||||
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
class FakeConsultantCommentsCubit extends Cubit<ConsultantCommentsState> implements ConsultantCommentsCubit {
|
class FakeConsultantCommentsCubit extends Cubit<ConsultantCommentsState>
|
||||||
|
implements ConsultantCommentsCubit {
|
||||||
FakeConsultantCommentsCubit(super.initialState);
|
FakeConsultantCommentsCubit(super.initialState);
|
||||||
@override
|
@override
|
||||||
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||||
@@ -60,9 +60,16 @@ void main() {
|
|||||||
</svg>
|
</svg>
|
||||||
''';
|
''';
|
||||||
|
|
||||||
final bundle = _FakeAssetBundle({'assets/icons/revert_product.svg': fakeSvg, 'assets/icons/edit.svg': fakeSvg});
|
final bundle = _FakeAssetBundle({
|
||||||
|
'assets/icons/revert_product.svg': fakeSvg,
|
||||||
|
'assets/icons/edit.svg': fakeSvg,
|
||||||
|
});
|
||||||
|
|
||||||
Widget wrap({required DiaryCubit diaryCubit, required ConsultantCommentsCubit consultantCommentsCubit, required Widget child}) {
|
Widget wrap({
|
||||||
|
required DiaryCubit diaryCubit,
|
||||||
|
required ConsultantCommentsCubit consultantCommentsCubit,
|
||||||
|
required Widget child,
|
||||||
|
}) {
|
||||||
return DefaultAssetBundle(
|
return DefaultAssetBundle(
|
||||||
bundle: bundle,
|
bundle: bundle,
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
@@ -77,7 +84,9 @@ void main() {
|
|||||||
home: MultiBlocProvider(
|
home: MultiBlocProvider(
|
||||||
providers: [
|
providers: [
|
||||||
BlocProvider<DiaryCubit>.value(value: diaryCubit),
|
BlocProvider<DiaryCubit>.value(value: diaryCubit),
|
||||||
BlocProvider<ConsultantCommentsCubit>.value(value: consultantCommentsCubit),
|
BlocProvider<ConsultantCommentsCubit>.value(
|
||||||
|
value: consultantCommentsCubit,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: Scaffold(body: child),
|
child: Scaffold(body: child),
|
||||||
),
|
),
|
||||||
@@ -85,17 +94,36 @@ void main() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
DiaryStateLoaded loadedState({required PortionModel portion, required bool consultantView}) {
|
DiaryStateLoaded loadedState({
|
||||||
|
required PortionModel portion,
|
||||||
|
required bool consultantView,
|
||||||
|
String date = '2025-01-01',
|
||||||
|
bool isDiaryFinished = false,
|
||||||
|
bool isDiaryCommented = false,
|
||||||
|
int closed = 0,
|
||||||
|
}) {
|
||||||
final diary = DiaryDayEntity(
|
final diary = DiaryDayEntity(
|
||||||
id: 1,
|
id: 1,
|
||||||
date: '2025-01-01',
|
date: date,
|
||||||
wakeup: '07:00',
|
wakeup: '07:00',
|
||||||
retire: '23:00',
|
retire: '23:00',
|
||||||
closed: 0,
|
closed: closed,
|
||||||
comment: '',
|
comment: '',
|
||||||
typeOfDiary: const TypeOfDiaryModel(hasChosenDiet: false, hasChosenPlan: true, diaryState: DiaryStateEnum.started),
|
typeOfDiary: const TypeOfDiaryModel(
|
||||||
|
hasChosenDiet: false,
|
||||||
|
hasChosenPlan: true,
|
||||||
|
diaryState: DiaryStateEnum.started,
|
||||||
|
),
|
||||||
meals: [
|
meals: [
|
||||||
MealModel(id: 1, time: '08:00', typeNumber: 1, satiety: 0, mood: 0, portions: [portion], photos: const []),
|
MealModel(
|
||||||
|
id: 1,
|
||||||
|
time: '08:00',
|
||||||
|
typeNumber: 1,
|
||||||
|
satiety: 0,
|
||||||
|
mood: 0,
|
||||||
|
portions: [portion],
|
||||||
|
photos: const [],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
chosenPlanId: 1,
|
chosenPlanId: 1,
|
||||||
dayOfChosenDiet: null,
|
dayOfChosenDiet: null,
|
||||||
@@ -103,8 +131,18 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const nutrition = NutritionDataModel(
|
const nutrition = NutritionDataModel(
|
||||||
dailyNutritionIntake: NutritionModel(energy: 0, proteins: 0, fats: 0, carbohydrates: 0),
|
dailyNutritionIntake: NutritionModel(
|
||||||
dailyNutritionRecommendation: NutritionModel(energy: 0, proteins: 0, fats: 0, carbohydrates: 0),
|
energy: 0,
|
||||||
|
proteins: 0,
|
||||||
|
fats: 0,
|
||||||
|
carbohydrates: 0,
|
||||||
|
),
|
||||||
|
dailyNutritionRecommendation: NutritionModel(
|
||||||
|
energy: 0,
|
||||||
|
proteins: 0,
|
||||||
|
fats: 0,
|
||||||
|
carbohydrates: 0,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return DiaryState.loaded(
|
return DiaryState.loaded(
|
||||||
@@ -113,13 +151,17 @@ void main() {
|
|||||||
selectedDay: DateTime(2025, 1, 1),
|
selectedDay: DateTime(2025, 1, 1),
|
||||||
selectedMeal: 1,
|
selectedMeal: 1,
|
||||||
isConsultantView: consultantView,
|
isConsultantView: consultantView,
|
||||||
isDiaryFinished: false,
|
isDiaryFinished: isDiaryFinished,
|
||||||
isDiaryCommented: false,
|
isDiaryCommented: isDiaryCommented,
|
||||||
consultantReview: ConsultantReviewModel(
|
consultantReview: ConsultantReviewModel(
|
||||||
planName: 'Plan',
|
planName: 'Plan',
|
||||||
planDuration: 10,
|
planDuration: 10,
|
||||||
currentPlanDay: 1,
|
currentPlanDay: 1,
|
||||||
consultantComments: ConsultantComments(dayComment: null, mealsComments: const [], portionsComments: const []),
|
consultantComments: ConsultantComments(
|
||||||
|
dayComment: null,
|
||||||
|
mealsComments: const [],
|
||||||
|
portionsComments: const [],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
as DiaryStateLoaded;
|
as DiaryStateLoaded;
|
||||||
@@ -140,9 +182,21 @@ void main() {
|
|||||||
DiaryState.loadedTomorrowWithDiet(
|
DiaryState.loadedTomorrowWithDiet(
|
||||||
diary: const DiaryFutureDietDayEntity(
|
diary: const DiaryFutureDietDayEntity(
|
||||||
date: '2025-01-02',
|
date: '2025-01-02',
|
||||||
typeOfDiary: TypeOfDiaryModel(hasChosenDiet: true, hasChosenPlan: false, diaryState: null),
|
typeOfDiary: TypeOfDiaryModel(
|
||||||
|
hasChosenDiet: true,
|
||||||
|
hasChosenPlan: false,
|
||||||
|
diaryState: null,
|
||||||
|
),
|
||||||
meals: [
|
meals: [
|
||||||
MealModel(id: 1, time: '08:00', typeNumber: 1, satiety: 0, mood: 0, portions: [portion], photos: []),
|
MealModel(
|
||||||
|
id: 1,
|
||||||
|
time: '08:00',
|
||||||
|
typeNumber: 1,
|
||||||
|
satiety: 0,
|
||||||
|
mood: 0,
|
||||||
|
portions: [portion],
|
||||||
|
photos: [],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
diet: null,
|
diet: null,
|
||||||
dayOfChosenDiet: 1,
|
dayOfChosenDiet: 1,
|
||||||
@@ -151,7 +205,11 @@ void main() {
|
|||||||
selectedMeal: 1,
|
selectedMeal: 1,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final commentsCubit = FakeConsultantCommentsCubit(ConsultantCommentsState(commentsModel: ConsultantCommentModel(comment: '')));
|
final commentsCubit = FakeConsultantCommentsCubit(
|
||||||
|
ConsultantCommentsState(
|
||||||
|
commentsModel: ConsultantCommentModel(comment: ''),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
wrap(
|
wrap(
|
||||||
@@ -177,8 +235,14 @@ void main() {
|
|||||||
fromDiet: false,
|
fromDiet: false,
|
||||||
portionState: PortionState.eatenDeleted,
|
portionState: PortionState.eatenDeleted,
|
||||||
);
|
);
|
||||||
final diaryCubit = FakeDiaryCubit(loadedState(portion: portion, consultantView: false));
|
final diaryCubit = FakeDiaryCubit(
|
||||||
final commentsCubit = FakeConsultantCommentsCubit(ConsultantCommentsState(commentsModel: ConsultantCommentModel(comment: '')));
|
loadedState(portion: portion, consultantView: false),
|
||||||
|
);
|
||||||
|
final commentsCubit = FakeConsultantCommentsCubit(
|
||||||
|
ConsultantCommentsState(
|
||||||
|
commentsModel: ConsultantCommentModel(comment: ''),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
wrap(
|
wrap(
|
||||||
@@ -194,32 +258,89 @@ void main() {
|
|||||||
expect(find.byType(ProductWidget), findsOneWidget);
|
expect(find.byType(ProductWidget), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('consultant view toggles comment widget and shows existing comment container', (tester) async {
|
testWidgets(
|
||||||
|
'consultant view toggles comment widget and shows existing comment container',
|
||||||
|
(tester) async {
|
||||||
|
const portion = PortionModel(
|
||||||
|
id: 1,
|
||||||
|
productNutritionalValueId: null,
|
||||||
|
productCaloriesInfo: null,
|
||||||
|
productName: 'Portion',
|
||||||
|
quantity: 100,
|
||||||
|
portion: 'gr',
|
||||||
|
fromDiet: false,
|
||||||
|
portionState: PortionState.filledDiet,
|
||||||
|
);
|
||||||
|
final state = loadedState(portion: portion, consultantView: true)
|
||||||
|
.copyWith(
|
||||||
|
consultantReview: ConsultantReviewModel(
|
||||||
|
planName: 'Plan',
|
||||||
|
planDuration: 10,
|
||||||
|
currentPlanDay: 1,
|
||||||
|
consultantComments: ConsultantComments(
|
||||||
|
dayComment: null,
|
||||||
|
mealsComments: const [],
|
||||||
|
portionsComments: [
|
||||||
|
PortionComment(portionId: 1, comment: 'Looks good'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final diaryCubit = FakeDiaryCubit(state);
|
||||||
|
final commentsCubit = FakeConsultantCommentsCubit(
|
||||||
|
ConsultantCommentsState(
|
||||||
|
commentsModel: ConsultantCommentModel(comment: ''),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
wrap(
|
||||||
|
diaryCubit: diaryCubit,
|
||||||
|
consultantCommentsCubit: commentsCubit,
|
||||||
|
child: const ProductWidget(portion: portion),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.byType(CommentContainer), findsOneWidget);
|
||||||
|
expect(find.text('Looks good'), findsOneWidget);
|
||||||
|
|
||||||
|
// Tap edit icon to enable PortionCommentWidget.
|
||||||
|
await tester.tap(find.byType(GestureDetector).first);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.byType(PortionCommentWidget), findsOneWidget);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
testWidgets('can edit a portion on a finished day inside the edit window', (
|
||||||
|
tester,
|
||||||
|
) async {
|
||||||
const portion = PortionModel(
|
const portion = PortionModel(
|
||||||
id: 1,
|
id: 1,
|
||||||
productNutritionalValueId: null,
|
productNutritionalValueId: null,
|
||||||
productCaloriesInfo: null,
|
productCaloriesInfo: null,
|
||||||
productName: 'Portion',
|
productName: 'Apple',
|
||||||
quantity: 100,
|
quantity: 100,
|
||||||
portion: 'gr',
|
portion: 'gr',
|
||||||
fromDiet: false,
|
fromDiet: false,
|
||||||
portionState: PortionState.filledDiet,
|
portionState: PortionState.eaten,
|
||||||
);
|
);
|
||||||
final state = loadedState(portion: portion, consultantView: true).copyWith(
|
final yesterday = DateTime.now().subtract(const Duration(days: 1));
|
||||||
consultantReview: ConsultantReviewModel(
|
final diaryCubit = FakeDiaryCubit(
|
||||||
planName: 'Plan',
|
loadedState(
|
||||||
planDuration: 10,
|
portion: portion,
|
||||||
currentPlanDay: 1,
|
consultantView: false,
|
||||||
consultantComments: ConsultantComments(
|
date: _calendarDate(yesterday),
|
||||||
dayComment: null,
|
isDiaryFinished: true,
|
||||||
mealsComments: const [],
|
),
|
||||||
portionsComments: [PortionComment(portionId: 1, comment: 'Looks good')],
|
);
|
||||||
),
|
final commentsCubit = FakeConsultantCommentsCubit(
|
||||||
|
ConsultantCommentsState(
|
||||||
|
commentsModel: ConsultantCommentModel(comment: ''),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final diaryCubit = FakeDiaryCubit(state);
|
|
||||||
final commentsCubit = FakeConsultantCommentsCubit(ConsultantCommentsState(commentsModel: ConsultantCommentModel(comment: '')));
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
wrap(
|
wrap(
|
||||||
@@ -230,15 +351,51 @@ void main() {
|
|||||||
);
|
);
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(tester.widget<ListTile>(find.byType(ListTile)).onTap, isNotNull);
|
||||||
|
});
|
||||||
|
|
||||||
expect(find.byType(CommentContainer), findsOneWidget);
|
testWidgets('cannot edit a portion on a commented day', (tester) async {
|
||||||
expect(find.text('Looks good'), findsOneWidget);
|
const portion = PortionModel(
|
||||||
|
id: 1,
|
||||||
|
productNutritionalValueId: null,
|
||||||
|
productCaloriesInfo: null,
|
||||||
|
productName: 'Apple',
|
||||||
|
quantity: 100,
|
||||||
|
portion: 'gr',
|
||||||
|
fromDiet: false,
|
||||||
|
portionState: PortionState.eaten,
|
||||||
|
);
|
||||||
|
final yesterday = DateTime.now().subtract(const Duration(days: 1));
|
||||||
|
final diaryCubit = FakeDiaryCubit(
|
||||||
|
loadedState(
|
||||||
|
portion: portion,
|
||||||
|
consultantView: false,
|
||||||
|
date: _calendarDate(yesterday),
|
||||||
|
isDiaryCommented: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final commentsCubit = FakeConsultantCommentsCubit(
|
||||||
|
ConsultantCommentsState(
|
||||||
|
commentsModel: ConsultantCommentModel(comment: ''),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// Tap edit icon to enable PortionCommentWidget.
|
await tester.pumpWidget(
|
||||||
await tester.tap(find.byType(GestureDetector).first);
|
wrap(
|
||||||
|
diaryCubit: diaryCubit,
|
||||||
|
consultantCommentsCubit: commentsCubit,
|
||||||
|
child: const ProductWidget(portion: portion),
|
||||||
|
),
|
||||||
|
);
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
|
|
||||||
expect(find.byType(PortionCommentWidget), findsOneWidget);
|
expect(tester.widget<ListTile>(find.byType(ListTile)).onTap, isNull);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _calendarDate(DateTime date) {
|
||||||
|
final month = date.month.toString().padLeft(2, '0');
|
||||||
|
final day = date.day.toString().padLeft(2, '0');
|
||||||
|
return '${date.year}-$month-$day';
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user