forked from eatvio/eatvio_app
feat(my_diets): fixed draft, active and personal diets creation, updating
This commit is contained in:
@@ -71,16 +71,16 @@ class OtherRoutes {
|
||||
try {
|
||||
final profileRepository = sl<ProfileRepository>();
|
||||
final profile = await profileRepository.getMyProfile();
|
||||
|
||||
|
||||
final firebaseApi = sl<FirebaseApi>();
|
||||
await firebaseApi.registerPushToken();
|
||||
await firebaseApi.registerPushToken();
|
||||
|
||||
if (profile.emailVerifiedAt != null || (prefs.getBool(PrefsKeys.isEmailVerificationSkipped) ?? false)) {
|
||||
return RouteConstants.feedScreen;
|
||||
}
|
||||
return RouteConstants.verifyEmailScreen;
|
||||
} catch (e) {
|
||||
return '/auth-screen';
|
||||
return '/auth-screen';
|
||||
}
|
||||
} else {
|
||||
return '/auth-screen';
|
||||
@@ -111,12 +111,12 @@ class OtherRoutes {
|
||||
// При любой ошибке пропускаем проверку версии
|
||||
return null;
|
||||
});
|
||||
|
||||
|
||||
if (versions == null) {
|
||||
// Если проверка не удалась, пропускаем экран обновления
|
||||
return RouteConstants.welcomeScreen;
|
||||
}
|
||||
|
||||
|
||||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||
String version = packageInfo.version;
|
||||
Version currentVersion = Version.parse(version);
|
||||
@@ -163,15 +163,16 @@ class OtherRoutes {
|
||||
final diet = data['diet'] as Diet?;
|
||||
final isEditing = data['isEditing'] as bool?;
|
||||
return CreateDietScreen(diet: diet, isEditing: isEditing);
|
||||
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: DietPreviewScreen.path,
|
||||
name: DietPreviewScreen.name,
|
||||
builder: (context, state) {
|
||||
final data = state.extra as Diet;
|
||||
return DietPreviewScreen(diet: data);
|
||||
final data = state.extra as Map<String, dynamic>;
|
||||
final diet = data['diet'] as Diet;
|
||||
final isImageInBase64 = data['isImageInBase64'] as bool;
|
||||
return DietPreviewScreen(diet: diet, isImageInBase64: isImageInBase64,);
|
||||
},
|
||||
),
|
||||
GoRoute(path: RouteConstants.createPlanScreen, name: 'createPlanScreen', builder: (context, state) => const CreatePlanScreen()),
|
||||
|
||||
@@ -8,32 +8,33 @@ import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class SuccessPublishedDietOrPlanBottomSheet extends StatelessWidget {
|
||||
const SuccessPublishedDietOrPlanBottomSheet({super.key, required this.title, this.isPlan = false});
|
||||
const SuccessPublishedDietOrPlanBottomSheet({super.key, required this.title, this.isPlan = false, this.isDrafted, this.isUpdated});
|
||||
final String title;
|
||||
final bool isPlan;
|
||||
final bool? isDrafted;
|
||||
final bool? isUpdated;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = S.of(context);
|
||||
final String text;
|
||||
if (isDrafted == true) {
|
||||
text = 'cохранена в черновиках';
|
||||
} else if (isUpdated == true) {
|
||||
text = 'успешно обновлена';
|
||||
} else if (isPlan == true) {
|
||||
text = 'опубликован';
|
||||
} else {
|
||||
text = 'опубликованна';
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const BottomSheetHandlerBar(),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
Text(
|
||||
isPlan ? 'опубликован' : 'опубликованна',
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(title, style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black)),
|
||||
Text(text, style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black)),
|
||||
const SizedBox(height: 20),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: CustomOutlinedButton(
|
||||
@@ -43,15 +44,10 @@ class SuccessPublishedDietOrPlanBottomSheet extends StatelessWidget {
|
||||
text: s.ok,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textSSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: const WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||
),
|
||||
buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.blueA400)),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -32,21 +32,21 @@ class CreateDietScreen extends StatelessWidget {
|
||||
const CreateDietScreen({super.key, this.diet, this.isEditing});
|
||||
|
||||
final Diet? diet;
|
||||
final bool? isEditing;
|
||||
final bool? isEditing;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => sl<CreateDietCubit>()..setInitialData(diet),
|
||||
child: CreateDietScreenContent(diet: diet),
|
||||
child: CreateDietScreenContent(diet: diet, isEditing: isEditing),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CreateDietScreenContent extends StatefulWidget {
|
||||
const CreateDietScreenContent({super.key, this.diet});
|
||||
const CreateDietScreenContent({super.key, this.diet, this.isEditing});
|
||||
|
||||
final Diet? diet;
|
||||
|
||||
final bool? isEditing;
|
||||
@override
|
||||
State<CreateDietScreenContent> createState() => _CreateDietScreenContentState();
|
||||
}
|
||||
@@ -88,7 +88,7 @@ class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
|
||||
context.pop();
|
||||
showBottomSheetWithBlur(
|
||||
context: context,
|
||||
child: SuccessPublishedDietOrPlanBottomSheet(title: '${state.dietData.name ?? 'Диета'} сохраненна в черновиках'),
|
||||
child: SuccessPublishedDietOrPlanBottomSheet(title: state.dietData.name ?? 'Диета', isDrafted: true),
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -182,14 +182,21 @@ class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
|
||||
children: [
|
||||
CustomOutlinedButton(
|
||||
onPressed: () async {
|
||||
context.read<CreateDietCubit>().setDietState(DietState.draft);
|
||||
if (widget.diet != null) {
|
||||
if (widget.diet != null && widget.isEditing == true) {
|
||||
if (state.dietState == DietState.draft) {
|
||||
context.read<CreateDietCubit>().setDietState(DietState.active);
|
||||
} else {
|
||||
context.read<CreateDietCubit>().setDietState(DietState.draft);
|
||||
}
|
||||
final result = await context.read<CreateDietCubit>().updateDiet(widget.diet!.id);
|
||||
if (result == 'error') {
|
||||
if (!context.mounted) return;
|
||||
showErrorDialog(context, title: '${s.could_not_update_diet} ${state.dietData.name}');
|
||||
}
|
||||
} else {
|
||||
if (state.dietState != DietState.draft) {
|
||||
context.read<CreateDietCubit>().setDietState(DietState.draft);
|
||||
}
|
||||
final result = await context.read<CreateDietCubit>().createDiet();
|
||||
if (result == 'error') {
|
||||
if (!context.mounted) return;
|
||||
@@ -197,15 +204,7 @@ class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
|
||||
}
|
||||
}
|
||||
},
|
||||
// onPressed: state.isDietPublishing
|
||||
// ? () {}
|
||||
// : () async {
|
||||
// final diet = await context.read<CreateDietCubit>().getDietForPreview();
|
||||
// if (diet == null) return;
|
||||
// if (!context.mounted) return;
|
||||
// GoRouter.of(context).pushNamed(DietPreviewScreen.name, extra: diet);
|
||||
// },
|
||||
text: "В черновик",
|
||||
text: state.dietState == DietState.draft && widget.isEditing == true ? s.publish : "В черновик",
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray),
|
||||
buttonStyle: ButtonStyle(
|
||||
@@ -221,7 +220,9 @@ class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
|
||||
final diet = await context.read<CreateDietCubit>().getDietForPreview();
|
||||
if (diet == null) return;
|
||||
if (!context.mounted) return;
|
||||
GoRouter.of(context).pushNamed(DietPreviewScreen.name, extra: diet);
|
||||
GoRouter.of(
|
||||
context,
|
||||
).pushNamed(DietPreviewScreen.name, extra: {'diet': diet, 'isImageInBase64': state.dietData.imageUrl == null});
|
||||
},
|
||||
text: s.preview,
|
||||
height: 52,
|
||||
@@ -237,13 +238,21 @@ class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
|
||||
else
|
||||
CustomOutlinedButton(
|
||||
onPressed: () async {
|
||||
if (widget.diet != null) {
|
||||
final result = await context.read<CreateDietCubit>().updateDiet(
|
||||
widget.diet!.id,
|
||||
);
|
||||
if (widget.diet != null && widget.isEditing == true) {
|
||||
final result = await context.read<CreateDietCubit>().updateDiet(widget.diet!.id);
|
||||
if (result == 'error') {
|
||||
if (!context.mounted) return;
|
||||
showErrorDialog(context, title: '${s.could_not_update_diet} ${state.dietData.name}');
|
||||
} else {
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
if (!context.mounted) return;
|
||||
context.pop();
|
||||
showBottomSheetWithBlur(
|
||||
userRootNavigator: true,
|
||||
context: context,
|
||||
child: SuccessPublishedDietOrPlanBottomSheet(title: state.dietData.name ?? 'Диета', isUpdated: true),
|
||||
);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
final result = await context.read<CreateDietCubit>().createDiet();
|
||||
@@ -253,7 +262,7 @@ class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
|
||||
}
|
||||
}
|
||||
},
|
||||
text: widget.diet != null ? s.save : s.publish,
|
||||
text: widget.diet != null && widget.isEditing == true ? s.save : s.publish,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
|
||||
@@ -49,6 +49,12 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
type['carb'] = diet.type.carb ?? false;
|
||||
type['light'] = diet.type.light ?? false;
|
||||
final appConfig = sl<AppConfig>();
|
||||
String? imageUrl;
|
||||
if (diet.image?.startsWith('https') == true) {
|
||||
imageUrl = diet.image;
|
||||
} else if (diet.image != null && diet.image?.isNotEmpty == true) {
|
||||
imageUrl = '${appConfig.baseUrl}/storage/${diet.image}';
|
||||
}
|
||||
emit(
|
||||
state.copyWith.dietData(
|
||||
dailySchedule: dailySchedule,
|
||||
@@ -57,8 +63,7 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
name: diet.name,
|
||||
duration: diet.duration,
|
||||
type: type,
|
||||
// Avoid network image URLs during tests to prevent HTTP fetch attempts
|
||||
imageUrl: appConfig.buildType == AppBuildType.tests ? null : '${appConfig.baseUrl}/storage/${diet.image}',
|
||||
imageUrl: appConfig.buildType == AppBuildType.tests ? null : imageUrl,
|
||||
),
|
||||
);
|
||||
emit(state.copyWith(dietState: diet.state, durationIndex: durationValues.indexOf(diet.duration)));
|
||||
@@ -196,7 +201,6 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
}
|
||||
|
||||
Future<Diet?> getDietForPreview() async {
|
||||
if (!_isFromValid()) return null;
|
||||
final authorProfile = await profileRepository.getMyProfile();
|
||||
final imageBytes = (await state.dietData.image?.readAsBytes()) ?? <int>[];
|
||||
String image = base64.encode(imageBytes);
|
||||
@@ -305,7 +309,10 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
|
||||
Future<String?> createDiet() async {
|
||||
emit(state.copyWith(isDietPublishing: true));
|
||||
if (!_isFromValid()) {
|
||||
if ([DietState.active, DietState.personal].contains(state.dietState) && !_isFromValid()) {
|
||||
emit(state.copyWith(isDietPublishing: false));
|
||||
return 'validation';
|
||||
} else if (!_isFormValidForDraft()) {
|
||||
emit(state.copyWith(isDietPublishing: false));
|
||||
return 'validation';
|
||||
}
|
||||
@@ -324,12 +331,12 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
name: state.dietData.name ?? '',
|
||||
duration: state.dietData.duration ?? 7,
|
||||
state: dietState,
|
||||
image: image ?? file!,
|
||||
image: image ?? file,
|
||||
meatType: state.dietData.type['meat'] == true ? '1' : '0',
|
||||
carbType: state.dietData.type['carb'] == true ? '1' : '0',
|
||||
vegType: state.dietData.type['veg'] == true ? '1' : '0',
|
||||
lightType: state.dietData.type['light'] == true ? '1' : '0',
|
||||
details: state.dietData.details ?? '',
|
||||
details: state.dietData.details,
|
||||
dailySchedule: dailySchedule ?? [],
|
||||
);
|
||||
|
||||
@@ -343,7 +350,12 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
}
|
||||
|
||||
Future<String?> updateDiet(int dietId) async {
|
||||
// if (!_isFromValid()) return 'error';
|
||||
if ([DietState.active, DietState.personal].contains(state.dietState) && !_isFromValid()) {
|
||||
return 'error';
|
||||
} else if (!_isFormValidForDraft()) {
|
||||
return 'error';
|
||||
}
|
||||
|
||||
try {
|
||||
File? image;
|
||||
if (state.dietData.image != null) {
|
||||
@@ -359,7 +371,7 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
carbType: state.dietData.type['carb'] == true ? '1' : '0',
|
||||
vegType: state.dietData.type['veg'] == true ? '1' : '0',
|
||||
lightType: state.dietData.type['light'] == true ? '1' : '0',
|
||||
details: state.dietData.details,
|
||||
details: state.dietData.details?.isEmpty ?? true ? null : state.dietData.details,
|
||||
dailySchedule: state.dietData.dailySchedule,
|
||||
);
|
||||
return null;
|
||||
@@ -369,6 +381,24 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
}
|
||||
}
|
||||
|
||||
bool _isFormValidForDraft() {
|
||||
bool result = true;
|
||||
if ((state.dietData.name?.length ?? 0) < 3) {
|
||||
result = false;
|
||||
emit(state.copyWith(dietNameError: 'Длинна названия не может быть меньше 3 символов'));
|
||||
}
|
||||
if (state.dietData.name == null || state.dietData.name == '') {
|
||||
result = false;
|
||||
emit(state.copyWith(dietNameError: 'Поле не заполнено'));
|
||||
}
|
||||
|
||||
if (state.dietData.details?.length != null && (state.dietData.details?.isNotEmpty ?? false) && (state.dietData.details?.length ?? 4) < 3) {
|
||||
result = false;
|
||||
emit(state.copyWith(dietDescriptionError: 'Длинна описания не может быть меньше 3 символов'));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool _isFromValid() {
|
||||
bool result = true;
|
||||
if ((state.dietData.name?.length ?? 0) < 3) {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/enums/product_type.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/utils/localization_utils.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_divider.dart';
|
||||
import 'package:eatvio_app/core/widgets/info_row.dart';
|
||||
@@ -18,10 +21,11 @@ import 'package:flutter_svg/svg.dart';
|
||||
|
||||
class DietPreviewScreen extends StatefulWidget {
|
||||
final Diet diet;
|
||||
final bool isImageInBase64;
|
||||
late final List<int> selectedIndexes;
|
||||
static const name = 'createDietScreenPreview';
|
||||
static const path = '/create-diet-screen/preview';
|
||||
DietPreviewScreen({super.key, required this.diet}) {
|
||||
DietPreviewScreen({super.key, required this.diet, required this.isImageInBase64}) {
|
||||
selectedIndexes = List.generate(diet.dailySchedule.length, (_) => 0);
|
||||
}
|
||||
|
||||
@@ -35,7 +39,7 @@ class _DietPreviewScreenState extends State<DietPreviewScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.diet.image != null) {
|
||||
if (widget.diet.image != null && widget.isImageInBase64) {
|
||||
imageData = base64.decode(widget.diet.image!);
|
||||
}
|
||||
}
|
||||
@@ -109,6 +113,54 @@ class _DietPreviewScreenState extends State<DietPreviewScreen> {
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
else if (!widget.isImageInBase64 && widget.diet.image != null && (widget.diet.image?.isNotEmpty ?? false))
|
||||
Stack(
|
||||
children: [
|
||||
CachedNetworkImage(
|
||||
imageUrl: widget.diet.image?.startsWith('https') == true
|
||||
? widget.diet.image!
|
||||
: '${sl<AppConfig>().baseUrl}/storage/${widget.diet.image}',
|
||||
height: 300,
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
Positioned(
|
||||
bottom: 16,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
InfoBouble(
|
||||
backgroundColor: appTheme.whiteA700,
|
||||
children: [
|
||||
SvgPicture.asset('assets/icons/eye.svg', width: 18, height: 12),
|
||||
const SizedBox(width: 8),
|
||||
Text(widget.diet.totalViews.toString(), style: TextStyles.textXSSemibold.copyWith(color: appTheme.black)),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
GestureDetector(
|
||||
onTap: () {},
|
||||
child: InfoBouble(
|
||||
backgroundColor: appTheme.whiteA700,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
'assets/icons/heart.svg',
|
||||
width: 14,
|
||||
height: 12,
|
||||
colorFilter: widget.diet.wasLikedByMe ? ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn) : null,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(widget.diet.totalLikes.toString(), style: TextStyles.textXSSemibold.copyWith(color: appTheme.black)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Padding(
|
||||
|
||||
@@ -185,7 +185,7 @@ class _AddMealWidgetState extends State<AddMealWidget> {
|
||||
dailyNutrition: DailyNutrition(energy: calories, proteins: proteins, fats: fats, carbohydrates: carbohydrates),
|
||||
quantity: int.parse(quantityController.text),
|
||||
name: glossary.name,
|
||||
productNutritionalValueId: glossary.id.toString(),
|
||||
productNutritionalValueId: glossary.id,
|
||||
portion: measurementUnits,
|
||||
);
|
||||
if (portionIndex == null) {
|
||||
@@ -212,7 +212,7 @@ class _AddMealWidgetState extends State<AddMealWidget> {
|
||||
dailyNutrition: DailyNutrition(energy: calories, proteins: proteins, fats: fats, carbohydrates: carbohydrates),
|
||||
quantity: int.parse(quantityController.text),
|
||||
name: newGlossary.name,
|
||||
productNutritionalValueId: newGlossary.id.toString(),
|
||||
productNutritionalValueId: newGlossary.id,
|
||||
portion: measurementUnits,
|
||||
);
|
||||
if (portionIndex == null) {
|
||||
|
||||
@@ -11,44 +11,56 @@ class CreateDietRepository {
|
||||
required String name,
|
||||
required int duration,
|
||||
required DietState state,
|
||||
required String meatType,
|
||||
required String carbType,
|
||||
required String vegType,
|
||||
required String lightType,
|
||||
required String details,
|
||||
required File image,
|
||||
required List<List<List<DailySchedule>>> dailySchedule,
|
||||
required String? meatType,
|
||||
required String? carbType,
|
||||
required String? vegType,
|
||||
required String? lightType,
|
||||
required String? details,
|
||||
required File? image,
|
||||
required List<List<List<DailySchedule>>>? dailySchedule,
|
||||
}) {
|
||||
final dietMap = <String, dynamic>{};
|
||||
for (final (dayIndex, day) in dailySchedule.indexed) {
|
||||
for (final (mealIndex, meal) in day.indexed) {
|
||||
for (final (productIndex, product) in meal.indexed) {
|
||||
final jsonProduct = product.toJson();
|
||||
dietMap.addAll({
|
||||
'daily_schedule[${dayIndex + 1}][${mealIndex + 1}][$productIndex][name]': jsonProduct['name'],
|
||||
'daily_schedule[${dayIndex + 1}][${mealIndex + 1}][$productIndex][portion]': jsonProduct['portion'],
|
||||
'daily_schedule[${dayIndex + 1}][${mealIndex + 1}][$productIndex][quantity]': jsonProduct['quantity'],
|
||||
'daily_schedule[${dayIndex + 1}][${mealIndex + 1}][$productIndex][product_nutritional_value_id]': jsonProduct['product_nutritional_value_id'],
|
||||
});
|
||||
if (dailySchedule != null) {
|
||||
for (final (dayIndex, day) in dailySchedule.indexed) {
|
||||
for (final (mealIndex, meal) in day.indexed) {
|
||||
for (final (productIndex, product) in meal.indexed) {
|
||||
final jsonProduct = product.toJson();
|
||||
dietMap.addAll({
|
||||
'daily_schedule[${dayIndex + 1}][${mealIndex + 1}][$productIndex][name]': jsonProduct['name'],
|
||||
'daily_schedule[${dayIndex + 1}][${mealIndex + 1}][$productIndex][portion]': jsonProduct['portion'],
|
||||
'daily_schedule[${dayIndex + 1}][${mealIndex + 1}][$productIndex][quantity]': jsonProduct['quantity'],
|
||||
'daily_schedule[${dayIndex + 1}][${mealIndex + 1}][$productIndex][product_nutritional_value_id]': jsonProduct['product_nutritional_value_id'],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (details != null) {
|
||||
dietMap.addAll({'description_md': details});
|
||||
}
|
||||
|
||||
dietMap.addAll({
|
||||
'name': name,
|
||||
'description_md': details,
|
||||
'duration': duration,
|
||||
'type[meat]': meatType,
|
||||
'type[carb]': carbType,
|
||||
'type[veg]': vegType,
|
||||
'type[light]': lightType,
|
||||
'image': MultipartFile.fromFileSync(
|
||||
image.path,
|
||||
filename: image.path.split(Platform.pathSeparator).last,
|
||||
),
|
||||
});
|
||||
if (meatType != null) {
|
||||
dietMap.addAll({'type[meat]': meatType});
|
||||
}
|
||||
|
||||
final formData = FormData.fromMap(dietMap);
|
||||
if (carbType != null) {
|
||||
dietMap.addAll({'type[carb]': carbType});
|
||||
}
|
||||
|
||||
if (vegType != null) {
|
||||
dietMap.addAll({'type[veg]': vegType});
|
||||
}
|
||||
|
||||
if (lightType != null) {
|
||||
dietMap.addAll({'type[light]': lightType});
|
||||
}
|
||||
if (image != null) {
|
||||
dietMap.addAll({'image': MultipartFile.fromFileSync(image.path, filename: image.path.split(Platform.pathSeparator).last)});
|
||||
}
|
||||
|
||||
dietMap.addAll({'name': name, 'duration': duration});
|
||||
|
||||
final formData = FormData.fromMap(dietMap);
|
||||
switch (state) {
|
||||
case DietState.active:
|
||||
return createDietService.createPublishedDiet(formData: formData);
|
||||
@@ -133,12 +145,7 @@ class CreateDietRepository {
|
||||
dietMap.addAll({'type[light]': lightType});
|
||||
}
|
||||
if (image != null) {
|
||||
dietMap.addAll({
|
||||
'image': MultipartFile.fromFileSync(
|
||||
image.path,
|
||||
filename: image.path.split(Platform.pathSeparator).last,
|
||||
),
|
||||
});
|
||||
dietMap.addAll({'image': MultipartFile.fromFileSync(image.path, filename: image.path.split(Platform.pathSeparator).last)});
|
||||
}
|
||||
|
||||
final formData = FormData.fromMap(dietMap);
|
||||
|
||||
@@ -182,7 +182,7 @@ class _AddPortionWidgetState extends State<AddPortionWidget> {
|
||||
dailyNutrition: DailyNutrition(energy: calories, proteins: proteins, fats: fats, carbohydrates: carbohydrates),
|
||||
quantity: int.parse(quantityController.text),
|
||||
name: glossary.name,
|
||||
productNutritionalValueId: glossary.id.toString(),
|
||||
productNutritionalValueId: glossary.id,
|
||||
portion: measurementUnits,
|
||||
);
|
||||
if (portionIndex == null) {
|
||||
@@ -212,7 +212,7 @@ class _AddPortionWidgetState extends State<AddPortionWidget> {
|
||||
dailyNutrition: DailyNutrition(energy: calories, proteins: proteins, fats: fats, carbohydrates: carbohydrates),
|
||||
quantity: int.parse(quantityController.text),
|
||||
name: newGlossary.name,
|
||||
productNutritionalValueId: newGlossary.id.toString(),
|
||||
productNutritionalValueId: newGlossary.id,
|
||||
portion: measurementUnits,
|
||||
);
|
||||
if (portionIndex == null) {
|
||||
|
||||
@@ -105,7 +105,7 @@ abstract class DailySchedule with _$DailySchedule {
|
||||
@JsonKey(name: "portion") required Portion portion,
|
||||
@JsonKey(name: "quantity", fromJson: _parseQuantity) required int quantity,
|
||||
@JsonKey(name: 'nutrition', includeToJson: false) DailyNutrition? dailyNutrition,
|
||||
@JsonKey(name: 'product_nutritional_value_id', includeToJson: true, includeFromJson: false, includeIfNull: false) String? productNutritionalValueId,
|
||||
@JsonKey(name: 'product_nutritional_value_id', includeToJson: true, includeFromJson: true, includeIfNull: false) int? productNutritionalValueId,
|
||||
}) = _DailySchedule;
|
||||
|
||||
factory DailySchedule.fromJson(Map<String, dynamic> json) => _$DailyScheduleFromJson(json);
|
||||
|
||||
@@ -234,7 +234,7 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
|
||||
2 => AddItemButton(
|
||||
onTap: state.profile?.roles.contains('consultant') ?? false
|
||||
? () async {
|
||||
await GoRouter.of(context).pushNamed(CreateDietScreen.name);
|
||||
await GoRouter.of(context).pushNamed(CreateDietScreen.name, extra: {'isEditing': false});
|
||||
}
|
||||
: null,
|
||||
),
|
||||
|
||||
@@ -107,7 +107,7 @@ class MyDietsScreenContent extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
AddItemButton(onTap: () => GoRouter.of(context).pushNamed(CreateDietScreen.name)),
|
||||
AddItemButton(onTap: () => GoRouter.of(context).pushNamed(CreateDietScreen.name, extra: {'isEditing': false})),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -251,7 +251,7 @@ class _MyDietWidgetState extends State<MyDietWidget> {
|
||||
Expanded(
|
||||
child: CustomOutlinedButton(
|
||||
onPressed: () async {
|
||||
GoRouter.of(context).pushNamed(DietPreviewScreen.name, extra: widget.diet);
|
||||
GoRouter.of(context).pushNamed(DietPreviewScreen.name, extra: {'diet': widget.diet, 'isImageInBase64': false});
|
||||
},
|
||||
text: 'Превью',
|
||||
height: 52,
|
||||
@@ -263,6 +263,22 @@ class _MyDietWidgetState extends State<MyDietWidget> {
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
CustomOutlinedButton(
|
||||
onPressed: () async {
|
||||
_deleteDiet(widget.diet, context);
|
||||
},
|
||||
height: 52,
|
||||
width: 52,
|
||||
leftIcon: SvgPicture.asset('assets/icons/trash_can.svg', width: 20, height: 20),
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.red),
|
||||
buttonStyle: ButtonStyle(
|
||||
shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.circular(20))),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.lightGray0),
|
||||
side: const WidgetStatePropertyAll(BorderSide.none),
|
||||
),
|
||||
text: '',
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user