forked from eatvio/eatvio_app
Merge pull request 'Plans creation has been completed' (#401) from misha/eatvio_app:main into main
Reviewed-on: eatvio/eatvio_app#401
This commit is contained in:
@@ -174,7 +174,10 @@ class AppRouter {
|
||||
GoRoute(
|
||||
path: CardSettingsScreen.path,
|
||||
name: CardSettingsScreen.name,
|
||||
builder: (context, state) => const CardSettingsScreen(),
|
||||
builder: (context, state) {
|
||||
final chosenPlanId = state.extra as int?;
|
||||
return CardSettingsScreen(chosenPlanId: chosenPlanId);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/about-plan-screen',
|
||||
@@ -220,7 +223,8 @@ class AppRouter {
|
||||
builder: (context, state) {
|
||||
final data = state.extra as Map<String, dynamic>;
|
||||
final chatId = data['chatId'] as String;
|
||||
return ChatScreen(chatId: chatId);
|
||||
final sendInitialMessage = data['sendInitialMessage'] as bool;
|
||||
return ChatScreen(chatId: chatId, sendInitialMessage: sendInitialMessage,);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
|
||||
@@ -19,6 +19,8 @@ import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_c
|
||||
import 'package:eatvio_app/features/create_diet/repositories/create_diet_repository.dart';
|
||||
import 'package:eatvio_app/features/create_diet/services/create_diet_service.dart';
|
||||
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart';
|
||||
import 'package:eatvio_app/features/create_plan/repositories/create_plan_repository.dart';
|
||||
import 'package:eatvio_app/features/create_plan/services/create_plan_service.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_cubit.dart';
|
||||
import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
|
||||
import 'package:eatvio_app/features/diets/services/diet_service.dart';
|
||||
@@ -117,7 +119,7 @@ Future<void> init() async {
|
||||
sl.registerFactory<BlogPostCreateCubit>(() => BlogPostCreateCubit(blogPostRepository: sl()));
|
||||
sl.registerFactory<SubCubit>(() => SubCubit(profileRepository: sl()));
|
||||
sl.registerFactory<CreateDietCubit>(() => CreateDietCubit(profileRepository: sl(), createDietRepository: sl(), glossaryRepository: sl()));
|
||||
sl.registerFactory<CreatePlanCubit>(() => CreatePlanCubit());
|
||||
sl.registerFactory<CreatePlanCubit>(() => CreatePlanCubit(createPlanRepository: sl()));
|
||||
sl.registerFactory<FeedProfileCubit>(() => FeedProfileCubit(profileRepository: sl()));
|
||||
sl.registerFactory<AboutPlanCubit>(() => AboutPlanCubit(planRepository: sl()));
|
||||
sl.registerFactory<CreateRecipeCubit>(() => CreateRecipeCubit(recipeRepository: sl()));
|
||||
@@ -158,6 +160,7 @@ Future<void> init() async {
|
||||
sl.registerLazySingleton<CardsRepository>(() => CardsRepository(cardsService: sl()));
|
||||
sl.registerLazySingleton<ConsultationsRepository>(() => ConsultationsRepository(consultationService: sl()));
|
||||
sl.registerLazySingleton<CancelPlanRepository>(() => CancelPlanRepository(cancelService: sl()));
|
||||
sl.registerLazySingleton<CreatePlanRepository>(() => CreatePlanRepository(createPlanService: sl()));
|
||||
|
||||
// Services
|
||||
sl.registerLazySingleton<ChatService>(() => ChatService(sl()));
|
||||
@@ -184,6 +187,7 @@ Future<void> init() async {
|
||||
sl.registerLazySingleton<CardsService>(() => CardsService(sl()));
|
||||
sl.registerLazySingleton<ConsultationService>(() => ConsultationService(sl()));
|
||||
sl.registerLazySingleton<CancelPlanService>(() => CancelPlanService(sl()));
|
||||
sl.registerLazySingleton<CreatePlanService>(() => CreatePlanService(sl()));
|
||||
|
||||
// External
|
||||
sl.registerLazySingleton(
|
||||
|
||||
@@ -147,6 +147,8 @@ class PrimaryColors {
|
||||
|
||||
Color get greenA70001 => const Color(0XFF00BE13);
|
||||
|
||||
Color get green => const Color(0XFF0BB450);
|
||||
|
||||
// LightGreen
|
||||
Color get lightGreen300 => const Color(0XFFB2DF8A);
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ class CustomTextFormField extends StatelessWidget {
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
autofocus: autofocus,
|
||||
style: const TextStyle(fontSize: 16, color: Colors.black, fontWeight: FontWeight.w500),
|
||||
style: textStyle ?? const TextStyle(fontSize: 16, color: Colors.black, fontWeight: FontWeight.w500),
|
||||
obscureText: obscureText!,
|
||||
textInputAction: textInputAction,
|
||||
maxLines: maxLines,
|
||||
@@ -115,20 +115,17 @@ class CustomTextFormField extends StatelessWidget {
|
||||
prefixIconConstraints: prefixConstraints,
|
||||
suffixIcon: suffix,
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
suffixStyle: textStyle ?? const TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: Color(0xFF323F4B)),
|
||||
suffixStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: Color(0xFF323F4B)),
|
||||
suffixIconConstraints: suffixConstraints,
|
||||
isDense: false,
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12),
|
||||
fillColor: const Color(0xFFF6F6F6),
|
||||
filled: true,
|
||||
border:
|
||||
borderDecoration ??
|
||||
border: borderDecoration ??
|
||||
OutlineInputBorder(borderRadius: BorderRadius.circular(20), borderSide: hasError ? BorderSide(color: appTheme.red, width: 1) : BorderSide.none),
|
||||
enabledBorder:
|
||||
borderDecoration ??
|
||||
enabledBorder: borderDecoration ??
|
||||
OutlineInputBorder(borderRadius: BorderRadius.circular(20), borderSide: hasError ? BorderSide(color: appTheme.red, width: 1) : BorderSide.none),
|
||||
focusedBorder:
|
||||
borderDecoration ??
|
||||
focusedBorder: borderDecoration ??
|
||||
OutlineInputBorder(borderRadius: BorderRadius.circular(20), borderSide: hasError ? BorderSide(color: appTheme.red, width: 1) : BorderSide.none),
|
||||
),
|
||||
keyboardType: keyboardType,
|
||||
|
||||
@@ -7,9 +7,10 @@ import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class SuccessPublishedDietBottomSheet extends StatelessWidget {
|
||||
const SuccessPublishedDietBottomSheet({super.key, required this.title});
|
||||
class SuccessPublishedDietOrPlanBottomSheet extends StatelessWidget {
|
||||
const SuccessPublishedDietOrPlanBottomSheet({super.key, required this.title, this.isPlan = false});
|
||||
final String title;
|
||||
final bool isPlan;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = S.of(context);
|
||||
@@ -27,7 +28,7 @@ class SuccessPublishedDietBottomSheet extends StatelessWidget {
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
Text(
|
||||
'опубликованна',
|
||||
isPlan ? 'опубликован' : 'опубликованна',
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
const SizedBox(
|
||||
|
||||
@@ -72,7 +72,7 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
|
||||
}
|
||||
}
|
||||
|
||||
initChat(String chatId) async {
|
||||
initChat(String chatId, {bool sendInitialMessage = false}) async {
|
||||
try {
|
||||
final chatRoom = await chatRepository.getChatRoom(chatId);
|
||||
ChosenPlanModel? chosenPlan;
|
||||
@@ -92,10 +92,14 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
|
||||
hasCard: false,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
if (chatRoom.chosenPlan?.consultantId == chatRoom.users.receiver.id) {
|
||||
await checkCards();
|
||||
}
|
||||
|
||||
if (sendInitialMessage) {
|
||||
await chatRepository.sendMessage(chatId: chatId, data: 'Здравствуйте, хочу заказать у вас: ${chosenPlan?.plan.name ?? 'план'}');
|
||||
}
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
typingSubscription?.cancel();
|
||||
|
||||
@@ -15,6 +15,7 @@ import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/features/chat/presentation/widgets/notification_banner.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
|
||||
import 'package:eatvio_app/features/order_info/presentation/info_by_order_screen.dart';
|
||||
import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
|
||||
import 'package:eatvio_app/features/user_details/presentation/user_details_screen.dart';
|
||||
@@ -28,7 +29,8 @@ import 'package:visibility_detector/visibility_detector.dart';
|
||||
|
||||
class ChatScreen extends StatefulWidget {
|
||||
final String chatId;
|
||||
const ChatScreen({required this.chatId, super.key});
|
||||
final bool sendInitialMessage;
|
||||
const ChatScreen({required this.chatId, super.key, required this.sendInitialMessage});
|
||||
|
||||
@override
|
||||
State<ChatScreen> createState() => _ChatScreenState();
|
||||
@@ -46,7 +48,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
scrollController.addListener(_onScroll);
|
||||
chatRoomCubit.initChat(widget.chatId);
|
||||
chatRoomCubit.initChat(widget.chatId, sendInitialMessage: widget.sendInitialMessage);
|
||||
}
|
||||
|
||||
_onScroll() {
|
||||
@@ -96,13 +98,24 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
BannerType? bannerType;
|
||||
if (chosenPlan == null) {
|
||||
bannerType = null;
|
||||
} else if (amIConsultant && state.hasCard) {
|
||||
} else if (!amIConsultant && !state.hasCard) {
|
||||
bannerType = BannerType.card;
|
||||
} else if (amIConsultant && chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateInCoordination) {
|
||||
bannerType = BannerType.date;
|
||||
} else if (!amIConsultant &&
|
||||
(chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateStartDateHasBeenSet || chosenPlan.chosenPlanState == ChosenPlanStateEnum.statePendingPayment)) {
|
||||
bannerType = BannerType.payment;
|
||||
} else if (chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateDone) {
|
||||
bannerType = BannerType.collectCharge;
|
||||
}
|
||||
|
||||
PacketOptions? chosenOption;
|
||||
if (chosenPlan?.optionType == 'options1') {
|
||||
chosenOption = chosenPlan?.plan.packet1Options;
|
||||
} else if (chosenPlan?.optionType == 'options2') {
|
||||
chosenOption = chosenPlan?.plan.packet2Options;
|
||||
} else if (chosenPlan?.optionType == 'options3') {
|
||||
chosenOption = chosenPlan?.plan.packet3Options;
|
||||
}
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: true,
|
||||
@@ -263,7 +276,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
itemCount: state.chatMessages.length + (state.isLoading ? 1 : 0),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (showUserDetailsButton) ...[
|
||||
if (!showUserDetailsButton) ...[
|
||||
// OrderCard(
|
||||
// plan: chosenPlan.plan,
|
||||
// isCustomerView: !amIConsultant,
|
||||
@@ -289,7 +302,15 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
),
|
||||
),
|
||||
if (bannerType != null)
|
||||
Padding(padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), child: NotificationBanner(bannerType: bannerType)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
child: NotificationBanner(
|
||||
bannerType: bannerType,
|
||||
chosenPlanId: chosenPlan?.id,
|
||||
planName: chosenPlan?.plan.name,
|
||||
planPrice: chosenOption?.price.toDouble(),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
|
||||
import 'package:eatvio_app/core/widgets/loader.dart';
|
||||
import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart';
|
||||
import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.dart';
|
||||
@@ -13,8 +14,11 @@ import 'package:flutter_svg/svg.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class NotificationBanner extends StatefulWidget {
|
||||
const NotificationBanner({super.key, required this.bannerType});
|
||||
const NotificationBanner({super.key, required this.bannerType, this.planPrice, this.planName, this.chosenPlanId});
|
||||
final BannerType bannerType;
|
||||
final double? planPrice;
|
||||
final String? planName;
|
||||
final int? chosenPlanId;
|
||||
|
||||
@override
|
||||
State<NotificationBanner> createState() => _NotificationBannerState();
|
||||
@@ -31,96 +35,145 @@ class _NotificationBannerState extends State<NotificationBanner> {
|
||||
color: appTheme.gray50,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12, right: 8, left: 12),
|
||||
child: SvgPicture.asset('assets/icons/reversed_exclamation_mark.svg', height: 68, width: 68),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (widget.bannerType != BannerType.collectCharge)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12, right: 8, left: 12),
|
||||
child: SvgPicture.asset('assets/icons/reversed_exclamation_mark.svg', height: 68, width: 68),
|
||||
)
|
||||
else
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(top: 12, right: 8, left: 12),
|
||||
child: _StarIcon(),
|
||||
),
|
||||
if (isLoading)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(left: 40, top: 24),
|
||||
child: Loader(),
|
||||
)
|
||||
else
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: switch (widget.bannerType) {
|
||||
BannerType.date => [
|
||||
SizedBox(
|
||||
width: size.width * 0.6,
|
||||
child: Text(s.choose_plan_start_date, style: TextStyles.textMMedium.copyWith(color: appTheme.black)),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
final cubit = context.read<ChatRoomCubit>();
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (context) => ChosePlanDateWidget(
|
||||
chatCubit: cubit,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Text(s.choose_date, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
BannerType.card => [
|
||||
SizedBox(
|
||||
width: size.width * 0.6,
|
||||
child: Text(s.can_not_choose_start_date, style: TextStyles.textMMedium.copyWith(color: appTheme.black)),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
GoRouter.of(context).pushReplacement(
|
||||
CardSettingsScreen.path,
|
||||
);
|
||||
},
|
||||
child: Text(s.add_card, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
BannerType.payment => [
|
||||
SizedBox(
|
||||
width: size.width * 0.6,
|
||||
child: Text(s.tap_on_link_to_pay, style: TextStyles.textMMedium.copyWith(color: appTheme.black)),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
final planId = (context.read<ChatRoomCubit>().state as ChatRoomStateLoaded).chatRoom.chosenPlan?.id;
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
final paymentLink = await context.read<ChatRoomCubit>().getPaymentLink();
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
if (paymentLink != null && planId != null) {
|
||||
if (!context.mounted) return;
|
||||
GoRouter.of(context).pushReplacement(
|
||||
PaymentScreen.path,
|
||||
extra: {'url': paymentLink, 'planId': planId},
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(s.pay, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
BannerType.collectCharge => [
|
||||
SizedBox(
|
||||
width: size.width * 0.6,
|
||||
child:
|
||||
Text('${s.congratsYouHaveFinishedPlan} «${widget.planName}»', style: TextStyles.textMSemibold.copyWith(color: appTheme.black)),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(
|
||||
width: size.width * 0.6,
|
||||
child: Text('${s.userConfirmedPlanCompletion} ${widget.planPrice}₽', style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (isLoading)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(left: 40, top: 24),
|
||||
child: Loader(),
|
||||
)
|
||||
else
|
||||
if (widget.bannerType == BannerType.collectCharge)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: switch (widget.bannerType) {
|
||||
BannerType.date => [
|
||||
SizedBox(
|
||||
width: size.width * 0.6,
|
||||
child: Text(s.choose_plan_start_date, style: TextStyles.textMMedium.copyWith(color: appTheme.black)),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
final cubit = context.read<ChatRoomCubit>();
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (context) => ChosePlanDateWidget(
|
||||
chatCubit: cubit,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Text(s.choose_date, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
BannerType.card => [
|
||||
SizedBox(
|
||||
width: size.width * 0.6,
|
||||
child: Text(s.can_not_choose_start_date, style: TextStyles.textMMedium.copyWith(color: appTheme.black)),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
GoRouter.of(context).pushReplacement(
|
||||
CardSettingsScreen.path,
|
||||
);
|
||||
},
|
||||
child: Text(s.add_card, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
BannerType.payment => [
|
||||
SizedBox(
|
||||
width: size.width * 0.6,
|
||||
child: Text(s.tap_on_link_to_pay, style: TextStyles.textMMedium.copyWith(color: appTheme.black)),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
final planId = (context.read<ChatRoomCubit>().state as ChatRoomStateLoaded).chatRoom.chosenPlan?.id;
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
final paymentLink = await context.read<ChatRoomCubit>().getPaymentLink();
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
if (paymentLink != null && planId != null) {
|
||||
if (!context.mounted) return;
|
||||
GoRouter.of(context).pushReplacement(
|
||||
PaymentScreen.path,
|
||||
extra: {'url': paymentLink, 'planId': planId},
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(s.pay, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: CustomOutlinedButton(
|
||||
text: s.collectCharge,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.green)),
|
||||
onPressed: () async {
|
||||
GoRouter.of(context).push(CardSettingsScreen.path, extra: widget.chosenPlanId);
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum BannerType { date, card, payment }
|
||||
class _StarIcon extends StatelessWidget {
|
||||
const _StarIcon();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(color: appTheme.whiteA700, shape: BoxShape.circle),
|
||||
child: SvgPicture.asset('assets/icons/star.svg'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum BannerType { date, card, payment, collectCharge }
|
||||
|
||||
@@ -72,7 +72,7 @@ class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
|
||||
showBottomSheetWithBlur(
|
||||
userRootNavigator: true,
|
||||
context: context,
|
||||
child: SuccessPublishedDietBottomSheet(
|
||||
child: SuccessPublishedDietOrPlanBottomSheet(
|
||||
title: state.dietData.name ?? 'Диета',
|
||||
),
|
||||
);
|
||||
@@ -83,7 +83,7 @@ class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
|
||||
context.pop();
|
||||
showBottomSheetWithBlur(
|
||||
context: context,
|
||||
child: SuccessPublishedDietBottomSheet(
|
||||
child: SuccessPublishedDietOrPlanBottomSheet(
|
||||
title: '${state.dietData.name ?? 'Диета'} сохраненна в черновиках',
|
||||
),
|
||||
);
|
||||
@@ -193,8 +193,8 @@ class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
|
||||
context.read<CreateDietCubit>().setDietState(DietState.draft);
|
||||
if (widget.diet != null) {
|
||||
final result = await context.read<CreateDietCubit>().updateDiet(
|
||||
widget.diet!.id,
|
||||
);
|
||||
widget.diet!.id,
|
||||
);
|
||||
if (result == 'error') {
|
||||
if (!context.mounted) return;
|
||||
showErrorDialog(context, title: '${s.could_not_update_diet} ${state.dietData.name}');
|
||||
|
||||
@@ -15,8 +15,8 @@ class CreatePlanModel with _$CreatePlanModel {
|
||||
const factory CreatePlanModel({
|
||||
String? name,
|
||||
String? details,
|
||||
String? state,
|
||||
int? duration,
|
||||
@Default('active') String? state,
|
||||
int? duration,
|
||||
required List<bool> type,
|
||||
XFile? image,
|
||||
required List<PacketOptions> options,
|
||||
@@ -25,9 +25,12 @@ class CreatePlanModel with _$CreatePlanModel {
|
||||
Future<Map<String, dynamic>> toCreationJson() async => {
|
||||
'name': name,
|
||||
'duration': duration,
|
||||
'state': state,
|
||||
'type': type,
|
||||
'state': 'active',
|
||||
'description_md': details,
|
||||
'type[slim]': type[0] ? '1' : '0',
|
||||
'type[fit]': type[1] ? '1' : '0',
|
||||
'type[wellness]': type[2] ? '1' : '0',
|
||||
'type[behavior]': type[3] ? '1' : '0',
|
||||
'image': image != null ? await MultipartFile.fromFile((await ImageCompressUtility.compressFile(File(image!.path))).path, filename: image!.name) : null,
|
||||
};
|
||||
|
||||
|
||||
11
lib/features/create_plan/models/plan_slug_model.dart
Normal file
11
lib/features/create_plan/models/plan_slug_model.dart
Normal file
@@ -0,0 +1,11 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'plan_slug_model.freezed.dart';
|
||||
part 'plan_slug_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class PlanSlugModel with _$PlanSlugModel {
|
||||
factory PlanSlugModel({@JsonKey(name: 'slug') required String slug}) = _PlanSlugModel;
|
||||
|
||||
factory PlanSlugModel.fromJson(Map<String, dynamic> json) => _$PlanSlugModelFromJson(json);
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/bottom_sheet_with_blur.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
||||
import 'package:eatvio_app/core/widgets/loader.dart';
|
||||
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
|
||||
import 'package:eatvio_app/core/widgets/success_published_diet_bottom_sheet.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart';
|
||||
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart';
|
||||
import 'package:eatvio_app/features/create_plan/presentation/widgets/option_widget.dart';
|
||||
@@ -15,6 +19,7 @@ import 'package:eatvio_app/features/create_plan/presentation/widgets/select_exac
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class CreatePlanScreen extends StatelessWidget {
|
||||
static const name = 'createPlanScreen';
|
||||
@@ -36,177 +41,229 @@ class CreatePlanScreenContent extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = S.of(context);
|
||||
return Scaffold(
|
||||
backgroundColor: appTheme.gray100,
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
s.adding_plan,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
),
|
||||
body: Stack(
|
||||
children: [
|
||||
SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
MainBackgroundBouble(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
s.label,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
CustomTextFormField(
|
||||
hintText: s.plan_label_desc,
|
||||
onChanged: (value) {
|
||||
context.read<CreatePlanCubit>().setName(value);
|
||||
},
|
||||
maxLength: 150,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
s.plan_duration,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
const PlanDurationDropDownWidget(),
|
||||
const SizedBox(height: 20),
|
||||
const PlanImagePickerWidget(),
|
||||
const SizedBox(height: 16),
|
||||
const PlanTypeChoiceWidget(),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
s.plan_desc,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
SizedBox(
|
||||
height: 132,
|
||||
child: CustomTextFormField(
|
||||
textAlignVertical: TextAlignVertical.top,
|
||||
maxLength: 1000,
|
||||
maxLines: null,
|
||||
minLines: null,
|
||||
expands: true,
|
||||
hintText: s.plan_desc_example,
|
||||
onChanged: (value) {
|
||||
context.read<CreatePlanCubit>().setDetails(value);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
child: Text(
|
||||
s.services,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.blueGray400),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const OptionsCountButton(),
|
||||
const SizedBox(height: 20),
|
||||
BlocBuilder<CreatePlanCubit, CreatePlanState>(
|
||||
buildWhen: (previous, current) => previous.selectedOptionIndex != current.selectedOptionIndex,
|
||||
builder: (context, state) {
|
||||
if (state.selectedOptionIndex == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
child: Text(
|
||||
'${s.option} ${(state.selectedOptionIndex ?? 0) + 1}',
|
||||
style: TextStyles.textLSemibold.copyWith(color: appTheme.blueGray400),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const OptionWidget(),
|
||||
const SizedBox(height: 20),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Column(
|
||||
children: [
|
||||
CustomOutlinedButton(
|
||||
onPressed: () {},
|
||||
text: s.to_draft,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: const WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.dottedBorder),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
CustomOutlinedButton(
|
||||
onPressed: () async {},
|
||||
text: s.preview,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: const WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.dottedBorder),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
CustomOutlinedButton(
|
||||
onPressed: () {},
|
||||
text: s.publish,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: const WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
return BlocBuilder<CreatePlanCubit, CreatePlanState>(
|
||||
buildWhen: (previous, current) => previous.planCreated != current.planCreated || previous.isLoading != current.isLoading,
|
||||
builder: (context, state) {
|
||||
if (state.planCreated) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!context.mounted) return;
|
||||
context.pop();
|
||||
showBottomSheetWithBlur(
|
||||
userRootNavigator: true,
|
||||
context: context,
|
||||
child: SuccessPublishedDietOrPlanBottomSheet(
|
||||
title: state.planData.name ?? 'План',
|
||||
isPlan: true,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
return Scaffold(
|
||||
backgroundColor: appTheme.gray100,
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
s.adding_plan,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
),
|
||||
BlocBuilder<CreatePlanCubit, CreatePlanState>(
|
||||
builder: (context, state) {
|
||||
if (state.selectedOptionIndex == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return const Padding(
|
||||
padding: EdgeInsets.only(bottom: 24.0),
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: SelectExactOptionWidget(),
|
||||
body: Stack(
|
||||
children: [
|
||||
SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
MainBackgroundBouble(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
s.label,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
BlocBuilder<CreatePlanCubit, CreatePlanState>(
|
||||
buildWhen: (previous, current) => previous.validationState != current.validationState,
|
||||
builder: (context, state) {
|
||||
return Column(
|
||||
children: [
|
||||
CustomTextFormField(
|
||||
hintText: s.plan_label_desc,
|
||||
hasError: state.validationState.titleHasError,
|
||||
onChanged: (value) {
|
||||
context.read<CreatePlanCubit>().setName(value);
|
||||
},
|
||||
maxLength: 150,
|
||||
),
|
||||
if (state.validationState.titleHasError) ...[
|
||||
const SizedBox(height: 4),
|
||||
ValidationErrorWidget(error: s.atLeast3Symbols),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
s.plan_duration,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
const PlanDurationDropDownWidget(),
|
||||
const SizedBox(height: 20),
|
||||
const PlanImagePickerWidget(),
|
||||
const SizedBox(height: 16),
|
||||
const PlanTypeChoiceWidget(),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
s.plan_desc,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
BlocBuilder<CreatePlanCubit, CreatePlanState>(
|
||||
buildWhen: (previous, current) => previous.validationState != current.validationState,
|
||||
builder: (context, state) {
|
||||
return Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 132,
|
||||
child: CustomTextFormField(
|
||||
hasError: state.validationState.descriptionHasError,
|
||||
textAlignVertical: TextAlignVertical.top,
|
||||
maxLength: 1000,
|
||||
maxLines: null,
|
||||
minLines: null,
|
||||
expands: true,
|
||||
hintText: s.plan_desc_example,
|
||||
onChanged: (value) {
|
||||
context.read<CreatePlanCubit>().setDetails(value);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (state.validationState.descriptionHasError) ...[
|
||||
const SizedBox(height: 4),
|
||||
ValidationErrorWidget(error: s.atLeast3Symbols),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
child: Text(
|
||||
s.services,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.blueGray400),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const OptionsCountButton(),
|
||||
const SizedBox(height: 20),
|
||||
BlocBuilder<CreatePlanCubit, CreatePlanState>(
|
||||
buildWhen: (previous, current) => previous.selectedOptionIndex != current.selectedOptionIndex,
|
||||
builder: (context, state) {
|
||||
if (state.selectedOptionIndex == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
child: Text(
|
||||
'${s.option} ${(state.selectedOptionIndex ?? 0) + 1}',
|
||||
style: TextStyles.textLSemibold.copyWith(color: appTheme.blueGray400),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const OptionWidget(),
|
||||
const SizedBox(height: 20),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Column(
|
||||
children: [
|
||||
// CustomOutlinedButton(
|
||||
// onPressed: () {},
|
||||
// text: s.to_draft,
|
||||
// height: 52,
|
||||
// buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray),
|
||||
// buttonStyle: ButtonStyle(
|
||||
// side: const WidgetStatePropertyAll(BorderSide.none),
|
||||
// backgroundColor: WidgetStatePropertyAll(appTheme.dottedBorder),
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 12),
|
||||
// CustomOutlinedButton(
|
||||
// onPressed: () async {},
|
||||
// text: s.preview,
|
||||
// height: 52,
|
||||
// buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray),
|
||||
// buttonStyle: ButtonStyle(
|
||||
// side: const WidgetStatePropertyAll(BorderSide.none),
|
||||
// backgroundColor: WidgetStatePropertyAll(appTheme.dottedBorder),
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 12),
|
||||
state.isLoading
|
||||
? const Loader()
|
||||
: CustomOutlinedButton(
|
||||
onPressed: () async {
|
||||
await context.read<CreatePlanCubit>().publishPlan();
|
||||
},
|
||||
text: s.publish,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: const WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 80),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
BlocBuilder<CreatePlanCubit, CreatePlanState>(
|
||||
builder: (context, state) {
|
||||
if (state.selectedOptionIndex == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return const Padding(
|
||||
padding: EdgeInsets.only(bottom: 24.0),
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: SelectExactOptionWidget(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +1,65 @@
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart';
|
||||
import 'package:eatvio_app/features/create_plan/repositories/create_plan_repository.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
||||
|
||||
class CreatePlanCubit extends Cubit<CreatePlanState> {
|
||||
CreatePlanCubit() : super(CreatePlanState.initial());
|
||||
final CreatePlanRepository createPlanRepository;
|
||||
CreatePlanCubit({required this.createPlanRepository}) : super(CreatePlanState.initial());
|
||||
|
||||
void setDetails(String text) {
|
||||
emit(state.copyWith(planData: state.planData.copyWith(details: text)));
|
||||
validateDetails();
|
||||
}
|
||||
|
||||
void setName(String name) {
|
||||
emit(state.copyWith(planData: state.planData.copyWith(name: name)));
|
||||
validateName();
|
||||
}
|
||||
|
||||
void setOptionTitle(String title) {
|
||||
final options = [...state.planData.options];
|
||||
options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(short: title);
|
||||
options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(short: title);
|
||||
emit(state.copyWith(planData: state.planData.copyWith(options: options)));
|
||||
validateSelectedOptionTitle();
|
||||
}
|
||||
|
||||
void setPrice(double price){
|
||||
void setPrice(double price) {
|
||||
final selectedOptionIndex = state.selectedOptionIndex ?? 0;
|
||||
final options = [...state.planData.options];
|
||||
options[selectedOptionIndex] = options[selectedOptionIndex].copyWith(price: price);
|
||||
final options = [...state.planData.options];
|
||||
options[selectedOptionIndex] = options[selectedOptionIndex].copyWith(price: price);
|
||||
emit(state.copyWith(planData: state.planData.copyWith(options: options)));
|
||||
validateSelectedOptionPrice();
|
||||
}
|
||||
|
||||
void setOptionDescription(String description) {
|
||||
final options = [...state.planData.options];
|
||||
options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(description: description);
|
||||
options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(description: description);
|
||||
emit(state.copyWith(planData: state.planData.copyWith(options: options)));
|
||||
validateSelectedOptionDescription();
|
||||
}
|
||||
|
||||
void setOptionContent(String content, index) {
|
||||
final options = [...state.planData.options];
|
||||
void setOptionContent(String content, index) {
|
||||
final options = [...state.planData.options];
|
||||
final items = [...options[state.selectedOptionIndex ?? 0].items];
|
||||
if (items.length <= index) {
|
||||
items.add(content);
|
||||
}else {
|
||||
} else {
|
||||
items[index] = content;
|
||||
}
|
||||
options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(items: items);
|
||||
options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(items: items);
|
||||
emit(state.copyWith(planData: state.planData.copyWith(options: options)));
|
||||
validateSelectedOptionItems();
|
||||
}
|
||||
|
||||
void removeOptionContentItem(int index) {
|
||||
final options = [...state.planData.options];
|
||||
final updatedItems = [...options[state.selectedOptionIndex ?? 0].items];
|
||||
updatedItems.removeAt(index);
|
||||
options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(items: updatedItems);
|
||||
options[state.selectedOptionIndex ?? 0] = options[state.selectedOptionIndex ?? 0].copyWith(items: updatedItems);
|
||||
emit(state.copyWith(planData: state.planData.copyWith(options: options)));
|
||||
validateSelectedOptionItems();
|
||||
}
|
||||
|
||||
void setDuration(int index) {
|
||||
@@ -62,10 +71,12 @@ class CreatePlanCubit extends Cubit<CreatePlanState> {
|
||||
),
|
||||
),
|
||||
);
|
||||
validateDuration();
|
||||
}
|
||||
|
||||
void setImage(XFile? image) {
|
||||
emit(state.copyWith(planData: state.planData.copyWith(image: image)));
|
||||
validateImage();
|
||||
}
|
||||
|
||||
void setPlanState(String planState) {
|
||||
@@ -74,9 +85,319 @@ class CreatePlanCubit extends Cubit<CreatePlanState> {
|
||||
|
||||
void setPlanType(List<bool> type) {
|
||||
emit(state.copyWith(planData: state.planData.copyWith(type: type)));
|
||||
if (type.any((elem) => elem)) {
|
||||
validateType();
|
||||
}
|
||||
}
|
||||
|
||||
void setSelectedOptionIndex(int? index) {
|
||||
emit(state.copyWith(selectedOptionIndex: index));
|
||||
}
|
||||
|
||||
bool validateName() {
|
||||
final name = state.planData.name;
|
||||
if (name == null || name.length < 3) {
|
||||
emit(state.copyWith.validationState(titleHasError: true));
|
||||
return false;
|
||||
}
|
||||
emit(state.copyWith.validationState(titleHasError: false));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validateDetails() {
|
||||
final details = state.planData.details;
|
||||
if (details == null || details.length < 3) {
|
||||
emit(state.copyWith.validationState(descriptionHasError: true));
|
||||
return false;
|
||||
}
|
||||
emit(state.copyWith.validationState(descriptionHasError: false));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validateDuration() {
|
||||
if (state.planData.duration == null) {
|
||||
emit(state.copyWith.validationState(durationHasError: true));
|
||||
return false;
|
||||
}
|
||||
emit(state.copyWith.validationState(durationHasError: false));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validateType() {
|
||||
final type = state.planData.type;
|
||||
if (type.isEmpty || type.every((elem) => elem)) {
|
||||
emit(state.copyWith.validationState(typeHasError: true));
|
||||
return false;
|
||||
}
|
||||
emit(state.copyWith.validationState(typeHasError: false));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validateImage() {
|
||||
if (state.planData.image == null) {
|
||||
emit(state.copyWith.validationState(imageHasError: true));
|
||||
return false;
|
||||
}
|
||||
emit(state.copyWith.validationState(imageHasError: false));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validateSelectedOptionPrice() {
|
||||
final option = state.planData.options[state.selectedOptionIndex ?? 0];
|
||||
final optionsValidationsStates = [...state.validationState.optionsValidationState];
|
||||
if (option.price < 1200) {
|
||||
optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(priceHasError: true);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
return false;
|
||||
}
|
||||
optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(priceHasError: false);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validatePriceOfAllFilledOptions() {
|
||||
final options = state.planData.options;
|
||||
final optionsValidationsStates = [...state.validationState.optionsValidationState];
|
||||
bool result = true;
|
||||
if (options[0].price < 1200) {
|
||||
optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(priceHasError: true);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
result = false;
|
||||
} else {
|
||||
optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(priceHasError: false);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
}
|
||||
|
||||
if (options[1].price < 1200) {
|
||||
optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(priceHasError: true);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
result = false;
|
||||
} else {
|
||||
optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(priceHasError: false);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
}
|
||||
|
||||
if (options[2].price < 1200) {
|
||||
optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(priceHasError: true);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
result = false;
|
||||
} else {
|
||||
optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(priceHasError: false);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool validateSelectedOptionItems() {
|
||||
final option = state.planData.options[state.selectedOptionIndex ?? 0];
|
||||
final optionsValidationsStates = [...state.validationState.optionsValidationState];
|
||||
bool result = true;
|
||||
if (option.items.isEmpty) {
|
||||
optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(isItemsEmpty: true);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
return false;
|
||||
}
|
||||
final itemsErrors = Map<int, bool>.from(optionsValidationsStates[state.selectedOptionIndex ?? 0].itemsErrors);
|
||||
optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(isItemsEmpty: false);
|
||||
for (final (index, item) in option.items.indexed) {
|
||||
if (item.length < 3) {
|
||||
itemsErrors[index] = true;
|
||||
result = false;
|
||||
} else {
|
||||
itemsErrors[index] = false;
|
||||
}
|
||||
}
|
||||
optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(itemsErrors: itemsErrors);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool validateItemsOfAllFilledOptions() {
|
||||
final options = state.planData.options;
|
||||
final optionsValidationsStates = [...state.validationState.optionsValidationState];
|
||||
bool result = true;
|
||||
|
||||
if (options[0].items.isEmpty) {
|
||||
optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(isItemsEmpty: true);
|
||||
result = false;
|
||||
} else {
|
||||
optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(isItemsEmpty: false);
|
||||
}
|
||||
|
||||
final option1ItemsErrors = Map<int, bool>.from(optionsValidationsStates[0].itemsErrors);
|
||||
for (final (index, item) in options[0].items.indexed) {
|
||||
if (item.length < 3) {
|
||||
option1ItemsErrors[index] = true;
|
||||
result = false;
|
||||
} else {
|
||||
option1ItemsErrors[index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(itemsErrors: option1ItemsErrors);
|
||||
if (state.selectedOptionIndex == null) {
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
return result;
|
||||
}
|
||||
if (options[1].items.isEmpty) {
|
||||
optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(isItemsEmpty: true);
|
||||
result = false;
|
||||
} else {
|
||||
optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(isItemsEmpty: false);
|
||||
}
|
||||
|
||||
final option2ItemsErrors = Map<int, bool>.from(optionsValidationsStates[1].itemsErrors);
|
||||
for (final (index, item) in options[1].items.indexed) {
|
||||
if (item.length < 3) {
|
||||
option2ItemsErrors[index] = true;
|
||||
result = false;
|
||||
} else {
|
||||
option2ItemsErrors[index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(itemsErrors: option2ItemsErrors);
|
||||
|
||||
if (options[2].items.isEmpty) {
|
||||
optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(isItemsEmpty: true);
|
||||
result = false;
|
||||
} else {
|
||||
optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(isItemsEmpty: false);
|
||||
}
|
||||
|
||||
final option3ItemsErrors = Map<int, bool>.from(optionsValidationsStates[2].itemsErrors);
|
||||
for (final (index, item) in options[2].items.indexed) {
|
||||
if (item.length < 3) {
|
||||
option3ItemsErrors[index] = true;
|
||||
result = false;
|
||||
} else {
|
||||
option3ItemsErrors[index] = false;
|
||||
}
|
||||
}
|
||||
optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(itemsErrors: option3ItemsErrors);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool validateSelectedOptionTitle() {
|
||||
final option = state.planData.options[state.selectedOptionIndex ?? 0];
|
||||
final optionsValidationsStates = [...state.validationState.optionsValidationState];
|
||||
if (option.short.length < 3) {
|
||||
optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(titleHasError: true);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
return false;
|
||||
}
|
||||
optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(titleHasError: false);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validateTitlesOfAllFilledOptions() {
|
||||
final options = state.planData.options;
|
||||
final optionsValidationsStates = [...state.validationState.optionsValidationState];
|
||||
bool result = true;
|
||||
if (options[0].short.length < 3) {
|
||||
optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(titleHasError: true);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
result = false;
|
||||
} else {
|
||||
optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(titleHasError: false);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
}
|
||||
if (state.selectedOptionIndex == null) return result;
|
||||
|
||||
if (options[1].short.length < 3) {
|
||||
optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(titleHasError: true);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
result = false;
|
||||
} else {
|
||||
optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(titleHasError: false);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
}
|
||||
|
||||
if (options[2].short.length < 3) {
|
||||
optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(titleHasError: true);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
result = false;
|
||||
} else {
|
||||
optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(titleHasError: false);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool validateSelectedOptionDescription() {
|
||||
final option = state.planData.options[state.selectedOptionIndex ?? 0];
|
||||
final optionsValidationsStates = [...state.validationState.optionsValidationState];
|
||||
if (option.description.length < 3) {
|
||||
optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(descriptionHasError: true);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
return false;
|
||||
}
|
||||
optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(descriptionHasError: false);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validateDescriptionsOfAllOptions() {
|
||||
final options = state.planData.options;
|
||||
final optionsValidationsStates = [...state.validationState.optionsValidationState];
|
||||
bool result = true;
|
||||
|
||||
if (options[0].description.length < 3) {
|
||||
optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(descriptionHasError: true);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
result = false;
|
||||
} else {
|
||||
optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(descriptionHasError: false);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
}
|
||||
if (state.selectedOptionIndex == null) return result;
|
||||
if (options[1].description.length < 3) {
|
||||
optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(descriptionHasError: true);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
result = false;
|
||||
} else {
|
||||
optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(descriptionHasError: false);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
}
|
||||
|
||||
if (options[2].description.length < 3) {
|
||||
optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(descriptionHasError: true);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
result = false;
|
||||
} else {
|
||||
optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(descriptionHasError: false);
|
||||
emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool validateAllFields() {
|
||||
final isNameValid = validateName();
|
||||
final isDetailsValid = validateDetails();
|
||||
final isImageValid = validateImage();
|
||||
final isDurationValid = validateDuration();
|
||||
final areTitlesValid = validateTitlesOfAllFilledOptions();
|
||||
final arePricesValid = validatePriceOfAllFilledOptions();
|
||||
final areDescriptionsValid = validateDescriptionsOfAllOptions();
|
||||
final areItemsValid = validateItemsOfAllFilledOptions();
|
||||
return isNameValid && isDetailsValid && isImageValid && isDurationValid && areTitlesValid && arePricesValid && areDescriptionsValid && areItemsValid;
|
||||
}
|
||||
|
||||
Future<void> publishPlan() async {
|
||||
if (validateAllFields()) {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
final planModel = await state.planData.toCreationJson();
|
||||
final plan = await createPlanRepository.createPlan(model: planModel);
|
||||
if (state.planData.options[1].short.isNotEmpty) {
|
||||
final options = state.planData.toOptionsCreationJson();
|
||||
await createPlanRepository.createPacketOptions(slug: plan.slug, model: options);
|
||||
} else {
|
||||
final option = state.planData.toOptionCreationJson();
|
||||
await createPlanRepository.createPacketOption(slug: plan.slug, model: option);
|
||||
}
|
||||
emit(state.copyWith(isLoading: false, planCreated: true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ class CreatePlanState with _$CreatePlanState {
|
||||
const factory CreatePlanState({
|
||||
required bool isLoading,
|
||||
required String? errorMessage,
|
||||
required CreatePlanModel planData,
|
||||
required CreatePlanModel planData,
|
||||
required PlanValidationState validationState,
|
||||
required bool planCreated,
|
||||
int? durationIndex,
|
||||
int? selectedOptionIndex,
|
||||
}) = _CreatePlanState;
|
||||
@@ -17,9 +19,55 @@ class CreatePlanState with _$CreatePlanState {
|
||||
factory CreatePlanState.initial() => CreatePlanState(
|
||||
isLoading: false,
|
||||
errorMessage: null,
|
||||
planCreated: false,
|
||||
validationState: PlanValidationState.empty(),
|
||||
planData: CreatePlanModel(
|
||||
type: [false, false, false, false],
|
||||
options: [PacketOptions.empty(), PacketOptions.empty(), PacketOptions.empty()],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class PlanValidationState with _$PlanValidationState {
|
||||
factory PlanValidationState({
|
||||
required bool titleHasError,
|
||||
required bool durationHasError,
|
||||
required bool typeHasError,
|
||||
required bool descriptionHasError,
|
||||
required bool imageHasError,
|
||||
required List<OptionValidationState> optionsValidationState,
|
||||
}) = _PlanValidationState;
|
||||
|
||||
factory PlanValidationState.empty() => PlanValidationState(
|
||||
titleHasError: false,
|
||||
durationHasError: false,
|
||||
typeHasError: false,
|
||||
descriptionHasError: false,
|
||||
imageHasError: false,
|
||||
optionsValidationState: [
|
||||
OptionValidationState.empty(),
|
||||
OptionValidationState.empty(),
|
||||
OptionValidationState.empty(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class OptionValidationState with _$OptionValidationState {
|
||||
factory OptionValidationState({
|
||||
required bool titleHasError,
|
||||
required bool priceHasError,
|
||||
required bool descriptionHasError,
|
||||
required Map<int, bool> itemsErrors,
|
||||
required bool isItemsEmpty,
|
||||
}) = _OptionValidationState;
|
||||
|
||||
factory OptionValidationState.empty() => OptionValidationState(
|
||||
descriptionHasError: false,
|
||||
titleHasError: false,
|
||||
priceHasError: false,
|
||||
isItemsEmpty: false,
|
||||
itemsErrors: {},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart';
|
||||
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -39,34 +41,53 @@ class _AddItemWidgetState extends State<AddItemWidget> {
|
||||
if (widget.items.length > widget.index && controller.text != widget.items[widget.index]) {
|
||||
controller.text = widget.items[widget.index];
|
||||
}
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomTextFormField(
|
||||
controller: controller,
|
||||
hintText: widget.enableHint ? s.option_content_hint_text : null,
|
||||
onChanged: (value) {
|
||||
context.read<CreatePlanCubit>().setOptionContent(value, widget.index);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (widget.canBeDeleted) ...[
|
||||
const SizedBox(width: 8),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
context.read<CreatePlanCubit>().removeOptionContentItem(widget.index);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(14),
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.lightGray0,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: SvgPicture.asset('assets/icons/trash_can.svg', width: 20, height: 24),
|
||||
return BlocBuilder<CreatePlanCubit, CreatePlanState>(
|
||||
buildWhen: (previous, current) => previous.validationState != current.validationState,
|
||||
builder: (context, state) {
|
||||
final hasError = state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].isItemsEmpty ||
|
||||
(state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].itemsErrors[widget.index] ?? false);
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: CustomTextFormField(
|
||||
controller: controller,
|
||||
hasError: hasError,
|
||||
hintText: widget.enableHint ? s.option_content_hint_text : null,
|
||||
onChanged: (value) {
|
||||
context.read<CreatePlanCubit>().setOptionContent(value, widget.index);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (widget.canBeDeleted) ...[
|
||||
const SizedBox(width: 8),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
context.read<CreatePlanCubit>().removeOptionContentItem(widget.index);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(14),
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.lightGray0,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: SvgPicture.asset('assets/icons/trash_can.svg', width: 20, height: 24),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
if (state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].isItemsEmpty) ...[
|
||||
const SizedBox(height: 4),
|
||||
ValidationErrorWidget(error: s.addAtLeastOneItem)
|
||||
] else if (state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].itemsErrors[widget.index] ?? false) ...[
|
||||
const SizedBox(height: 4),
|
||||
ValidationErrorWidget(error: s.atLeast3Symbols)
|
||||
]
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_divider.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
||||
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart';
|
||||
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart';
|
||||
import 'package:eatvio_app/features/create_plan/presentation/widgets/add_item_widget.dart';
|
||||
@@ -32,35 +33,9 @@ class _OptionWidgetState extends State<OptionWidget> {
|
||||
super.initState();
|
||||
customerPriceController.addListener(() {
|
||||
if (!customerPriceController.text.endsWith('₽')) {
|
||||
final textWithoutCurrency = customerPriceController.text.replaceAll('₽', '');
|
||||
customerPriceController.value = TextEditingValue(
|
||||
text: "$textWithoutCurrency₽",
|
||||
selection: TextSelection.collapsed(offset: textWithoutCurrency.length),
|
||||
);
|
||||
}
|
||||
final price = double.tryParse(customerPriceController.text.replaceAll('₽', ''));
|
||||
if (price != null && price < 1200) {
|
||||
customerPriceController.value = const TextEditingValue(
|
||||
text: "1200₽",
|
||||
selection: TextSelection.collapsed(offset: 4),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
creatorPriceController.addListener(() {
|
||||
if (!creatorPriceController.text.endsWith('₽')) {
|
||||
final textWithoutCurrency = creatorPriceController.text.replaceAll('₽', '');
|
||||
creatorPriceController.value = TextEditingValue(
|
||||
text: "$textWithoutCurrency₽",
|
||||
selection: TextSelection.collapsed(offset: textWithoutCurrency.length),
|
||||
);
|
||||
}
|
||||
final price = double.tryParse(creatorPriceController.text.replaceAll('₽', ''));
|
||||
if (price != null && price < 1000) {
|
||||
creatorPriceController.value = const TextEditingValue(
|
||||
text: "1000₽",
|
||||
selection: TextSelection.collapsed(offset: 4),
|
||||
);
|
||||
final text = customerPriceController.text.replaceAll('₽', '');
|
||||
customerPriceController.text = '$text₽';
|
||||
customerPriceController.selection = TextSelection.collapsed(offset: text.length);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -94,12 +69,17 @@ class _OptionWidgetState extends State<OptionWidget> {
|
||||
const SizedBox(height: 4),
|
||||
CustomTextFormField(
|
||||
hintText: optionNameHintText,
|
||||
hasError: state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].titleHasError,
|
||||
controller: optionTitleController,
|
||||
onChanged: (value) {
|
||||
context.read<CreatePlanCubit>().setOptionTitle(value);
|
||||
},
|
||||
maxLength: 150,
|
||||
),
|
||||
if (state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].titleHasError) ...[
|
||||
const SizedBox(height: 4),
|
||||
ValidationErrorWidget(error: s.atLeast3Symbols),
|
||||
],
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
s.option_desc,
|
||||
@@ -114,6 +94,7 @@ class _OptionWidgetState extends State<OptionWidget> {
|
||||
child: CustomTextFormField(
|
||||
controller: optionDescriptionController,
|
||||
textAlignVertical: TextAlignVertical.top,
|
||||
hasError: state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].descriptionHasError,
|
||||
maxLength: 1000,
|
||||
minLines: null,
|
||||
maxLines: null,
|
||||
@@ -124,6 +105,10 @@ class _OptionWidgetState extends State<OptionWidget> {
|
||||
},
|
||||
),
|
||||
),
|
||||
if (state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].descriptionHasError) ...[
|
||||
const SizedBox(height: 4),
|
||||
ValidationErrorWidget(error: s.atLeast3Symbols),
|
||||
],
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
@@ -141,14 +126,8 @@ class _OptionWidgetState extends State<OptionWidget> {
|
||||
const SizedBox(height: 4),
|
||||
CustomTextFormField(
|
||||
controller: creatorPriceController,
|
||||
onChanged: (value) {
|
||||
double? price = double.tryParse(value.replaceAll('₽', ''));
|
||||
if (price != null) {
|
||||
price = price * 1.2;
|
||||
customerPriceController.text = price.toStringAsFixed(2);
|
||||
context.read<CreatePlanCubit>().setPrice(price);
|
||||
}
|
||||
},
|
||||
readOnly: true,
|
||||
textStyle: TextStyle(color: appTheme.gray),
|
||||
maxLength: 20,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
@@ -169,10 +148,11 @@ class _OptionWidgetState extends State<OptionWidget> {
|
||||
CustomTextFormField(
|
||||
controller: customerPriceController,
|
||||
textInputType: const TextInputType.numberWithOptions(decimal: true),
|
||||
hasError: state.validationState.optionsValidationState[state.selectedOptionIndex ?? 0].priceHasError,
|
||||
onChanged: (value) {
|
||||
final price = double.tryParse(value.replaceAll('₽', ''));
|
||||
creatorPriceController.text = '${((price ?? 0) * 0.8).toStringAsFixed(2)}₽';
|
||||
if (price != null) {
|
||||
creatorPriceController.text = (price * 0.8).toStringAsFixed(2);
|
||||
context.read<CreatePlanCubit>().setPrice(price);
|
||||
}
|
||||
},
|
||||
@@ -235,33 +215,33 @@ class _OptionWidgetState extends State<OptionWidget> {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const CustomDivider(),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: isChecked,
|
||||
activeColor: appTheme.gray,
|
||||
checkColor: appTheme.gray,
|
||||
fillColor: const WidgetStatePropertyAll(Colors.transparent),
|
||||
side: WidgetStateBorderSide.resolveWith(
|
||||
(Set<WidgetState> states) {
|
||||
return BorderSide(width: 1, color: appTheme.gray);
|
||||
},
|
||||
),
|
||||
onChanged: (bool? value) {
|
||||
setState(() {
|
||||
isChecked = value ?? false;
|
||||
});
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
s.option_personal_diet_desc,
|
||||
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// const SizedBox(height: 16),
|
||||
// Row(
|
||||
// children: [
|
||||
// Checkbox(
|
||||
// value: isChecked,
|
||||
// activeColor: appTheme.gray,
|
||||
// checkColor: appTheme.gray,
|
||||
// fillColor: const WidgetStatePropertyAll(Colors.transparent),
|
||||
// side: WidgetStateBorderSide.resolveWith(
|
||||
// (Set<WidgetState> states) {
|
||||
// return BorderSide(width: 1, color: appTheme.gray);
|
||||
// },
|
||||
// ),
|
||||
// onChanged: (bool? value) {
|
||||
// setState(() {
|
||||
// isChecked = value ?? false;
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
// Expanded(
|
||||
// child: Text(
|
||||
// s.option_personal_diet_desc,
|
||||
// style: TextStyles.textMMedium.copyWith(color: appTheme.black),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/utils/localization_utils.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart';
|
||||
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
@@ -36,28 +37,37 @@ class _PlanDurationDropDownWidgetState extends State<PlanDurationDropDownWidget>
|
||||
final s = S.of(context);
|
||||
return BlocBuilder<CreatePlanCubit, CreatePlanState>(
|
||||
builder: (context, state) {
|
||||
return CustomDropDownButton(
|
||||
hint: Text(
|
||||
s.choose_plan_duration,
|
||||
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
context.read<CreatePlanCubit>().setDuration(durationDropDownTitles.indexOf(value));
|
||||
}
|
||||
},
|
||||
items: durationDropDownTitles
|
||||
.mapIndexed(
|
||||
(index, value) => DropdownMenuItem(
|
||||
value: durationDropDownTitles[index],
|
||||
child: Text(
|
||||
durationDropDownTitles[index],
|
||||
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
value: state.durationIndex == null ? null : durationDropDownTitles[state.durationIndex ?? 0],
|
||||
return Column(
|
||||
children: [
|
||||
CustomDropDownButton(
|
||||
hasError: state.validationState.durationHasError,
|
||||
hint: Text(
|
||||
s.choose_plan_duration,
|
||||
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
context.read<CreatePlanCubit>().setDuration(durationDropDownTitles.indexOf(value));
|
||||
}
|
||||
},
|
||||
items: durationDropDownTitles
|
||||
.mapIndexed(
|
||||
(index, value) => DropdownMenuItem(
|
||||
value: durationDropDownTitles[index],
|
||||
child: Text(
|
||||
durationDropDownTitles[index],
|
||||
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
value: state.durationIndex == null ? null : durationDropDownTitles[state.durationIndex ?? 0],
|
||||
),
|
||||
if (state.validationState.durationHasError) ...[
|
||||
const SizedBox(height: 4),
|
||||
ValidationErrorWidget(error: s.choose_plan_duration),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:io';
|
||||
import 'package:dotted_border/dotted_border.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart';
|
||||
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
@@ -72,74 +73,79 @@ class _PlanImagePickerWidgetState extends State<PlanImagePickerWidget> {
|
||||
final s = S.of(context);
|
||||
return BlocBuilder<CreatePlanCubit, CreatePlanState>(
|
||||
builder: (context, state) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
_showImagePickerOptions(s, context.read<CreatePlanCubit>());
|
||||
},
|
||||
child: Container(
|
||||
height: 240,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
image: state.planData.image != null ? DecorationImage(fit: BoxFit.cover, image: FileImage(File(state.planData.image?.path ?? ''))) : null,
|
||||
color: appTheme.gray50,
|
||||
return Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
_showImagePickerOptions(s, context.read<CreatePlanCubit>());
|
||||
},
|
||||
child: Container(
|
||||
height: 240,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
image: state.planData.image != null ? DecorationImage(fit: BoxFit.cover, image: FileImage(File(state.planData.image?.path ?? ''))) : null,
|
||||
color: appTheme.gray50,
|
||||
),
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
padding: state.planData.image != null ? null : const EdgeInsets.all(8),
|
||||
child: state.planData.image != null
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(top: 6, right: 6),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
context.read<CreatePlanCubit>().setImage(null);
|
||||
},
|
||||
child: Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: Container(
|
||||
width: 34,
|
||||
height: 34,
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.dividerColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.close_rounded,
|
||||
color: Colors.grey,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Stack(
|
||||
children: [
|
||||
DottedBorder(
|
||||
stackFit: StackFit.expand,
|
||||
dashPattern: const [14, 14],
|
||||
color: state.validationState.imageHasError ? appTheme.red : appTheme.dottedBorder,
|
||||
borderType: BorderType.RRect,
|
||||
radius: const Radius.circular(12),
|
||||
child: const SizedBox.expand(),
|
||||
),
|
||||
Center(
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 48,
|
||||
),
|
||||
SvgPicture.asset('assets/icons/eatvio_logo.svg'),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
Text(
|
||||
s.add_cover,
|
||||
style: TextStyles.textMMedium.copyWith(color: appTheme.gray),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
padding: state.planData.image != null ? null : const EdgeInsets.all(8),
|
||||
child: state.planData.image != null
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(top: 6, right: 6),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
context.read<CreatePlanCubit>().setImage(null);
|
||||
},
|
||||
child: Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: Container(
|
||||
width: 34,
|
||||
height: 34,
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.dividerColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.close_rounded,
|
||||
color: Colors.grey,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Stack(
|
||||
children: [
|
||||
DottedBorder(
|
||||
stackFit: StackFit.expand,
|
||||
dashPattern: const [14, 14],
|
||||
color: appTheme.dottedBorder,
|
||||
borderType: BorderType.RRect,
|
||||
radius: const Radius.circular(12),
|
||||
child: const SizedBox.expand(),
|
||||
),
|
||||
Center(
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 48,
|
||||
),
|
||||
SvgPicture.asset('assets/icons/eatvio_logo.svg'),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
Text(
|
||||
s.add_cover,
|
||||
style: TextStyles.textMMedium.copyWith(color: appTheme.gray),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (state.validationState.imageHasError) ...[const SizedBox(height: 8), ValidationErrorWidget(error: s.add_cover)],
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart';
|
||||
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
@@ -50,49 +51,55 @@ class _PlanTypeChoiceWidgetState extends State<PlanTypeChoiceWidget> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = S.of(context);
|
||||
return BlocBuilder<CreatePlanCubit, CreatePlanState>(
|
||||
builder: (context, state) {
|
||||
return Row(
|
||||
return Column(
|
||||
children: [
|
||||
...List.generate(4, (index) {
|
||||
return Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
final planTypes = [...state.planData.type];
|
||||
planTypes[index] = !planTypes[index];
|
||||
context.read<CreatePlanCubit>().setPlanType(planTypes);
|
||||
},
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(right: index == 3 ? 0 : 4),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: state.planData.type[index] ? appTheme.cardBackground : appTheme.whiteA700,
|
||||
border: Border.all(
|
||||
width: 1,
|
||||
color: state.planData.type[index] ? appTheme.blueA400 : appTheme.lightGray00,
|
||||
Row(
|
||||
children: [
|
||||
...List.generate(4, (index) {
|
||||
return Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
final planTypes = [...state.planData.type];
|
||||
planTypes[index] = !planTypes[index];
|
||||
context.read<CreatePlanCubit>().setPlanType(planTypes);
|
||||
},
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(right: index == 3 ? 0 : 4),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: state.planData.type[index] ? appTheme.cardBackground : appTheme.whiteA700,
|
||||
border: Border.all(
|
||||
width: 1,
|
||||
color: state.planData.type[index] ? appTheme.blueA400 : appTheme.lightGray00,
|
||||
),
|
||||
),
|
||||
height: 104,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
Image.asset(
|
||||
choiceContent[index].$1,
|
||||
height: 30,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
choiceContent[index].$2,
|
||||
overflow: TextOverflow.clip,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyles.textFS10FW500.copyWith(color: appTheme.black),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
height: 104,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
Image.asset(
|
||||
choiceContent[index].$1,
|
||||
height: 30,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
choiceContent[index].$2,
|
||||
overflow: TextOverflow.clip,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyles.textFS10FW500.copyWith(color: appTheme.black),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
if (state.validationState.typeHasError) ...[const SizedBox(height: 8), ValidationErrorWidget(error: s.chosePlanType)]
|
||||
],
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/features/create_plan/models/plan_slug_model.dart';
|
||||
import 'package:eatvio_app/features/create_plan/services/create_plan_service.dart';
|
||||
|
||||
class CreatePlanRepository {
|
||||
@@ -5,10 +7,11 @@ class CreatePlanRepository {
|
||||
|
||||
CreatePlanRepository({required this.createPlanService});
|
||||
|
||||
Future<void> createPlan({
|
||||
Future<PlanSlugModel> createPlan({
|
||||
required Map<String, dynamic> model,
|
||||
}) async {
|
||||
createPlanService.createPlan(model: model);
|
||||
final formData = FormData.fromMap(model);
|
||||
return await createPlanService.createPlan(formData: formData);
|
||||
}
|
||||
|
||||
Future<void> createPacketOption({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/features/create_plan/models/plan_slug_model.dart';
|
||||
import 'package:retrofit/retrofit.dart';
|
||||
|
||||
part 'create_plan_service.g.dart';
|
||||
@@ -9,8 +10,8 @@ abstract class CreatePlanService {
|
||||
|
||||
@POST('/api/plans/create')
|
||||
@MultiPart()
|
||||
Future<void> createPlan({
|
||||
@Body() required Map<String, dynamic> model,
|
||||
Future<PlanSlugModel> createPlan({
|
||||
@Body() required FormData formData,
|
||||
});
|
||||
|
||||
@POST('/api/plans/create/{slug}/packet_option')
|
||||
@@ -19,7 +20,7 @@ abstract class CreatePlanService {
|
||||
@Body() required Map<String, dynamic> model,
|
||||
});
|
||||
|
||||
@POST('/api/plans/create/{slug}/packet_options')
|
||||
@POST('/api/plans/create/{slug}/packet_options')
|
||||
Future<void> createPacketOptions({
|
||||
@Path() required String slug,
|
||||
@Body() required Map<String, dynamic> model,
|
||||
|
||||
@@ -36,6 +36,7 @@ class MealComment with _$MealComment {
|
||||
|
||||
factory MealComment({
|
||||
required String comment,
|
||||
required int typeNo,
|
||||
}) = _MealComment;
|
||||
|
||||
factory MealComment.fromJson(Map<String, dynamic> json) => _$MealCommentFromJson(json);
|
||||
@@ -45,7 +46,7 @@ class MealComment with _$MealComment {
|
||||
class PortionComment with _$PortionComment {
|
||||
|
||||
factory PortionComment({
|
||||
@JsonKey(name: 'commentable_id') required int portionId,
|
||||
@JsonKey(name: 'id') required int portionId,
|
||||
required String comment,
|
||||
}) = _PortionComment;
|
||||
|
||||
|
||||
813
lib/features/diary/models/consultant_review_model.freezed.dart
Normal file
813
lib/features/diary/models/consultant_review_model.freezed.dart
Normal file
@@ -0,0 +1,813 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'consultant_review_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
ConsultantReviewModel _$ConsultantReviewModelFromJson(
|
||||
Map<String, dynamic> json) {
|
||||
return _ConsultantReviewModel.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ConsultantReviewModel {
|
||||
String get planName => throw _privateConstructorUsedError;
|
||||
int get planDuration => throw _privateConstructorUsedError;
|
||||
int get currentPlanDay => throw _privateConstructorUsedError;
|
||||
ConsultantComments get consultantComments =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this ConsultantReviewModel to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of ConsultantReviewModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$ConsultantReviewModelCopyWith<ConsultantReviewModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ConsultantReviewModelCopyWith<$Res> {
|
||||
factory $ConsultantReviewModelCopyWith(ConsultantReviewModel value,
|
||||
$Res Function(ConsultantReviewModel) then) =
|
||||
_$ConsultantReviewModelCopyWithImpl<$Res, ConsultantReviewModel>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{String planName,
|
||||
int planDuration,
|
||||
int currentPlanDay,
|
||||
ConsultantComments consultantComments});
|
||||
|
||||
$ConsultantCommentsCopyWith<$Res> get consultantComments;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ConsultantReviewModelCopyWithImpl<$Res,
|
||||
$Val extends ConsultantReviewModel>
|
||||
implements $ConsultantReviewModelCopyWith<$Res> {
|
||||
_$ConsultantReviewModelCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of ConsultantReviewModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? planName = null,
|
||||
Object? planDuration = null,
|
||||
Object? currentPlanDay = null,
|
||||
Object? consultantComments = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
planName: null == planName
|
||||
? _value.planName
|
||||
: planName // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
planDuration: null == planDuration
|
||||
? _value.planDuration
|
||||
: planDuration // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
currentPlanDay: null == currentPlanDay
|
||||
? _value.currentPlanDay
|
||||
: currentPlanDay // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
consultantComments: null == consultantComments
|
||||
? _value.consultantComments
|
||||
: consultantComments // ignore: cast_nullable_to_non_nullable
|
||||
as ConsultantComments,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
/// Create a copy of ConsultantReviewModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$ConsultantCommentsCopyWith<$Res> get consultantComments {
|
||||
return $ConsultantCommentsCopyWith<$Res>(_value.consultantComments,
|
||||
(value) {
|
||||
return _then(_value.copyWith(consultantComments: value) as $Val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$ConsultantReviewModelImplCopyWith<$Res>
|
||||
implements $ConsultantReviewModelCopyWith<$Res> {
|
||||
factory _$$ConsultantReviewModelImplCopyWith(
|
||||
_$ConsultantReviewModelImpl value,
|
||||
$Res Function(_$ConsultantReviewModelImpl) then) =
|
||||
__$$ConsultantReviewModelImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{String planName,
|
||||
int planDuration,
|
||||
int currentPlanDay,
|
||||
ConsultantComments consultantComments});
|
||||
|
||||
@override
|
||||
$ConsultantCommentsCopyWith<$Res> get consultantComments;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$ConsultantReviewModelImplCopyWithImpl<$Res>
|
||||
extends _$ConsultantReviewModelCopyWithImpl<$Res,
|
||||
_$ConsultantReviewModelImpl>
|
||||
implements _$$ConsultantReviewModelImplCopyWith<$Res> {
|
||||
__$$ConsultantReviewModelImplCopyWithImpl(_$ConsultantReviewModelImpl _value,
|
||||
$Res Function(_$ConsultantReviewModelImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of ConsultantReviewModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? planName = null,
|
||||
Object? planDuration = null,
|
||||
Object? currentPlanDay = null,
|
||||
Object? consultantComments = null,
|
||||
}) {
|
||||
return _then(_$ConsultantReviewModelImpl(
|
||||
planName: null == planName
|
||||
? _value.planName
|
||||
: planName // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
planDuration: null == planDuration
|
||||
? _value.planDuration
|
||||
: planDuration // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
currentPlanDay: null == currentPlanDay
|
||||
? _value.currentPlanDay
|
||||
: currentPlanDay // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
consultantComments: null == consultantComments
|
||||
? _value.consultantComments
|
||||
: consultantComments // ignore: cast_nullable_to_non_nullable
|
||||
as ConsultantComments,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$ConsultantReviewModelImpl implements _ConsultantReviewModel {
|
||||
_$ConsultantReviewModelImpl(
|
||||
{required this.planName,
|
||||
required this.planDuration,
|
||||
required this.currentPlanDay,
|
||||
required this.consultantComments});
|
||||
|
||||
factory _$ConsultantReviewModelImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$ConsultantReviewModelImplFromJson(json);
|
||||
|
||||
@override
|
||||
final String planName;
|
||||
@override
|
||||
final int planDuration;
|
||||
@override
|
||||
final int currentPlanDay;
|
||||
@override
|
||||
final ConsultantComments consultantComments;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ConsultantReviewModel(planName: $planName, planDuration: $planDuration, currentPlanDay: $currentPlanDay, consultantComments: $consultantComments)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$ConsultantReviewModelImpl &&
|
||||
(identical(other.planName, planName) ||
|
||||
other.planName == planName) &&
|
||||
(identical(other.planDuration, planDuration) ||
|
||||
other.planDuration == planDuration) &&
|
||||
(identical(other.currentPlanDay, currentPlanDay) ||
|
||||
other.currentPlanDay == currentPlanDay) &&
|
||||
(identical(other.consultantComments, consultantComments) ||
|
||||
other.consultantComments == consultantComments));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, planName, planDuration, currentPlanDay, consultantComments);
|
||||
|
||||
/// Create a copy of ConsultantReviewModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ConsultantReviewModelImplCopyWith<_$ConsultantReviewModelImpl>
|
||||
get copyWith => __$$ConsultantReviewModelImplCopyWithImpl<
|
||||
_$ConsultantReviewModelImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$ConsultantReviewModelImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _ConsultantReviewModel implements ConsultantReviewModel {
|
||||
factory _ConsultantReviewModel(
|
||||
{required final String planName,
|
||||
required final int planDuration,
|
||||
required final int currentPlanDay,
|
||||
required final ConsultantComments consultantComments}) =
|
||||
_$ConsultantReviewModelImpl;
|
||||
|
||||
factory _ConsultantReviewModel.fromJson(Map<String, dynamic> json) =
|
||||
_$ConsultantReviewModelImpl.fromJson;
|
||||
|
||||
@override
|
||||
String get planName;
|
||||
@override
|
||||
int get planDuration;
|
||||
@override
|
||||
int get currentPlanDay;
|
||||
@override
|
||||
ConsultantComments get consultantComments;
|
||||
|
||||
/// Create a copy of ConsultantReviewModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$ConsultantReviewModelImplCopyWith<_$ConsultantReviewModelImpl>
|
||||
get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
ConsultantComments _$ConsultantCommentsFromJson(Map<String, dynamic> json) {
|
||||
return _ConsultantComments.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ConsultantComments {
|
||||
String? get dayComment => throw _privateConstructorUsedError;
|
||||
List<MealComment> get mealsComments => throw _privateConstructorUsedError;
|
||||
List<PortionComment> get portionsComments =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this ConsultantComments to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of ConsultantComments
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$ConsultantCommentsCopyWith<ConsultantComments> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ConsultantCommentsCopyWith<$Res> {
|
||||
factory $ConsultantCommentsCopyWith(
|
||||
ConsultantComments value, $Res Function(ConsultantComments) then) =
|
||||
_$ConsultantCommentsCopyWithImpl<$Res, ConsultantComments>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{String? dayComment,
|
||||
List<MealComment> mealsComments,
|
||||
List<PortionComment> portionsComments});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ConsultantCommentsCopyWithImpl<$Res, $Val extends ConsultantComments>
|
||||
implements $ConsultantCommentsCopyWith<$Res> {
|
||||
_$ConsultantCommentsCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of ConsultantComments
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? dayComment = freezed,
|
||||
Object? mealsComments = null,
|
||||
Object? portionsComments = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
dayComment: freezed == dayComment
|
||||
? _value.dayComment
|
||||
: dayComment // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
mealsComments: null == mealsComments
|
||||
? _value.mealsComments
|
||||
: mealsComments // ignore: cast_nullable_to_non_nullable
|
||||
as List<MealComment>,
|
||||
portionsComments: null == portionsComments
|
||||
? _value.portionsComments
|
||||
: portionsComments // ignore: cast_nullable_to_non_nullable
|
||||
as List<PortionComment>,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$ConsultantCommentsImplCopyWith<$Res>
|
||||
implements $ConsultantCommentsCopyWith<$Res> {
|
||||
factory _$$ConsultantCommentsImplCopyWith(_$ConsultantCommentsImpl value,
|
||||
$Res Function(_$ConsultantCommentsImpl) then) =
|
||||
__$$ConsultantCommentsImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{String? dayComment,
|
||||
List<MealComment> mealsComments,
|
||||
List<PortionComment> portionsComments});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$ConsultantCommentsImplCopyWithImpl<$Res>
|
||||
extends _$ConsultantCommentsCopyWithImpl<$Res, _$ConsultantCommentsImpl>
|
||||
implements _$$ConsultantCommentsImplCopyWith<$Res> {
|
||||
__$$ConsultantCommentsImplCopyWithImpl(_$ConsultantCommentsImpl _value,
|
||||
$Res Function(_$ConsultantCommentsImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of ConsultantComments
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? dayComment = freezed,
|
||||
Object? mealsComments = null,
|
||||
Object? portionsComments = null,
|
||||
}) {
|
||||
return _then(_$ConsultantCommentsImpl(
|
||||
dayComment: freezed == dayComment
|
||||
? _value.dayComment
|
||||
: dayComment // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
mealsComments: null == mealsComments
|
||||
? _value._mealsComments
|
||||
: mealsComments // ignore: cast_nullable_to_non_nullable
|
||||
as List<MealComment>,
|
||||
portionsComments: null == portionsComments
|
||||
? _value._portionsComments
|
||||
: portionsComments // ignore: cast_nullable_to_non_nullable
|
||||
as List<PortionComment>,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$ConsultantCommentsImpl implements _ConsultantComments {
|
||||
_$ConsultantCommentsImpl(
|
||||
{required this.dayComment,
|
||||
required final List<MealComment> mealsComments,
|
||||
required final List<PortionComment> portionsComments})
|
||||
: _mealsComments = mealsComments,
|
||||
_portionsComments = portionsComments;
|
||||
|
||||
factory _$ConsultantCommentsImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$ConsultantCommentsImplFromJson(json);
|
||||
|
||||
@override
|
||||
final String? dayComment;
|
||||
final List<MealComment> _mealsComments;
|
||||
@override
|
||||
List<MealComment> get mealsComments {
|
||||
if (_mealsComments is EqualUnmodifiableListView) return _mealsComments;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_mealsComments);
|
||||
}
|
||||
|
||||
final List<PortionComment> _portionsComments;
|
||||
@override
|
||||
List<PortionComment> get portionsComments {
|
||||
if (_portionsComments is EqualUnmodifiableListView)
|
||||
return _portionsComments;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_portionsComments);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ConsultantComments(dayComment: $dayComment, mealsComments: $mealsComments, portionsComments: $portionsComments)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$ConsultantCommentsImpl &&
|
||||
(identical(other.dayComment, dayComment) ||
|
||||
other.dayComment == dayComment) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._mealsComments, _mealsComments) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._portionsComments, _portionsComments));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
dayComment,
|
||||
const DeepCollectionEquality().hash(_mealsComments),
|
||||
const DeepCollectionEquality().hash(_portionsComments));
|
||||
|
||||
/// Create a copy of ConsultantComments
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ConsultantCommentsImplCopyWith<_$ConsultantCommentsImpl> get copyWith =>
|
||||
__$$ConsultantCommentsImplCopyWithImpl<_$ConsultantCommentsImpl>(
|
||||
this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$ConsultantCommentsImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _ConsultantComments implements ConsultantComments {
|
||||
factory _ConsultantComments(
|
||||
{required final String? dayComment,
|
||||
required final List<MealComment> mealsComments,
|
||||
required final List<PortionComment> portionsComments}) =
|
||||
_$ConsultantCommentsImpl;
|
||||
|
||||
factory _ConsultantComments.fromJson(Map<String, dynamic> json) =
|
||||
_$ConsultantCommentsImpl.fromJson;
|
||||
|
||||
@override
|
||||
String? get dayComment;
|
||||
@override
|
||||
List<MealComment> get mealsComments;
|
||||
@override
|
||||
List<PortionComment> get portionsComments;
|
||||
|
||||
/// Create a copy of ConsultantComments
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$ConsultantCommentsImplCopyWith<_$ConsultantCommentsImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
MealComment _$MealCommentFromJson(Map<String, dynamic> json) {
|
||||
return _MealComment.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$MealComment {
|
||||
String get comment => throw _privateConstructorUsedError;
|
||||
int get typeNo => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this MealComment to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of MealComment
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$MealCommentCopyWith<MealComment> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $MealCommentCopyWith<$Res> {
|
||||
factory $MealCommentCopyWith(
|
||||
MealComment value, $Res Function(MealComment) then) =
|
||||
_$MealCommentCopyWithImpl<$Res, MealComment>;
|
||||
@useResult
|
||||
$Res call({String comment, int typeNo});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$MealCommentCopyWithImpl<$Res, $Val extends MealComment>
|
||||
implements $MealCommentCopyWith<$Res> {
|
||||
_$MealCommentCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of MealComment
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? comment = null,
|
||||
Object? typeNo = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
comment: null == comment
|
||||
? _value.comment
|
||||
: comment // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
typeNo: null == typeNo
|
||||
? _value.typeNo
|
||||
: typeNo // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$MealCommentImplCopyWith<$Res>
|
||||
implements $MealCommentCopyWith<$Res> {
|
||||
factory _$$MealCommentImplCopyWith(
|
||||
_$MealCommentImpl value, $Res Function(_$MealCommentImpl) then) =
|
||||
__$$MealCommentImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({String comment, int typeNo});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$MealCommentImplCopyWithImpl<$Res>
|
||||
extends _$MealCommentCopyWithImpl<$Res, _$MealCommentImpl>
|
||||
implements _$$MealCommentImplCopyWith<$Res> {
|
||||
__$$MealCommentImplCopyWithImpl(
|
||||
_$MealCommentImpl _value, $Res Function(_$MealCommentImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of MealComment
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? comment = null,
|
||||
Object? typeNo = null,
|
||||
}) {
|
||||
return _then(_$MealCommentImpl(
|
||||
comment: null == comment
|
||||
? _value.comment
|
||||
: comment // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
typeNo: null == typeNo
|
||||
? _value.typeNo
|
||||
: typeNo // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$MealCommentImpl implements _MealComment {
|
||||
_$MealCommentImpl({required this.comment, required this.typeNo});
|
||||
|
||||
factory _$MealCommentImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$MealCommentImplFromJson(json);
|
||||
|
||||
@override
|
||||
final String comment;
|
||||
@override
|
||||
final int typeNo;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MealComment(comment: $comment, typeNo: $typeNo)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$MealCommentImpl &&
|
||||
(identical(other.comment, comment) || other.comment == comment) &&
|
||||
(identical(other.typeNo, typeNo) || other.typeNo == typeNo));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, comment, typeNo);
|
||||
|
||||
/// Create a copy of MealComment
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$MealCommentImplCopyWith<_$MealCommentImpl> get copyWith =>
|
||||
__$$MealCommentImplCopyWithImpl<_$MealCommentImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$MealCommentImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _MealComment implements MealComment {
|
||||
factory _MealComment(
|
||||
{required final String comment,
|
||||
required final int typeNo}) = _$MealCommentImpl;
|
||||
|
||||
factory _MealComment.fromJson(Map<String, dynamic> json) =
|
||||
_$MealCommentImpl.fromJson;
|
||||
|
||||
@override
|
||||
String get comment;
|
||||
@override
|
||||
int get typeNo;
|
||||
|
||||
/// Create a copy of MealComment
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$MealCommentImplCopyWith<_$MealCommentImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
PortionComment _$PortionCommentFromJson(Map<String, dynamic> json) {
|
||||
return _PortionComment.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$PortionComment {
|
||||
@JsonKey(name: 'id')
|
||||
int get portionId => throw _privateConstructorUsedError;
|
||||
String get comment => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this PortionComment to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of PortionComment
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$PortionCommentCopyWith<PortionComment> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $PortionCommentCopyWith<$Res> {
|
||||
factory $PortionCommentCopyWith(
|
||||
PortionComment value, $Res Function(PortionComment) then) =
|
||||
_$PortionCommentCopyWithImpl<$Res, PortionComment>;
|
||||
@useResult
|
||||
$Res call({@JsonKey(name: 'id') int portionId, String comment});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$PortionCommentCopyWithImpl<$Res, $Val extends PortionComment>
|
||||
implements $PortionCommentCopyWith<$Res> {
|
||||
_$PortionCommentCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of PortionComment
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? portionId = null,
|
||||
Object? comment = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
portionId: null == portionId
|
||||
? _value.portionId
|
||||
: portionId // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
comment: null == comment
|
||||
? _value.comment
|
||||
: comment // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$PortionCommentImplCopyWith<$Res>
|
||||
implements $PortionCommentCopyWith<$Res> {
|
||||
factory _$$PortionCommentImplCopyWith(_$PortionCommentImpl value,
|
||||
$Res Function(_$PortionCommentImpl) then) =
|
||||
__$$PortionCommentImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({@JsonKey(name: 'id') int portionId, String comment});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$PortionCommentImplCopyWithImpl<$Res>
|
||||
extends _$PortionCommentCopyWithImpl<$Res, _$PortionCommentImpl>
|
||||
implements _$$PortionCommentImplCopyWith<$Res> {
|
||||
__$$PortionCommentImplCopyWithImpl(
|
||||
_$PortionCommentImpl _value, $Res Function(_$PortionCommentImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of PortionComment
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? portionId = null,
|
||||
Object? comment = null,
|
||||
}) {
|
||||
return _then(_$PortionCommentImpl(
|
||||
portionId: null == portionId
|
||||
? _value.portionId
|
||||
: portionId // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
comment: null == comment
|
||||
? _value.comment
|
||||
: comment // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$PortionCommentImpl implements _PortionComment {
|
||||
_$PortionCommentImpl(
|
||||
{@JsonKey(name: 'id') required this.portionId, required this.comment});
|
||||
|
||||
factory _$PortionCommentImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$PortionCommentImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey(name: 'id')
|
||||
final int portionId;
|
||||
@override
|
||||
final String comment;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'PortionComment(portionId: $portionId, comment: $comment)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$PortionCommentImpl &&
|
||||
(identical(other.portionId, portionId) ||
|
||||
other.portionId == portionId) &&
|
||||
(identical(other.comment, comment) || other.comment == comment));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, portionId, comment);
|
||||
|
||||
/// Create a copy of PortionComment
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$PortionCommentImplCopyWith<_$PortionCommentImpl> get copyWith =>
|
||||
__$$PortionCommentImplCopyWithImpl<_$PortionCommentImpl>(
|
||||
this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$PortionCommentImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _PortionComment implements PortionComment {
|
||||
factory _PortionComment(
|
||||
{@JsonKey(name: 'id') required final int portionId,
|
||||
required final String comment}) = _$PortionCommentImpl;
|
||||
|
||||
factory _PortionComment.fromJson(Map<String, dynamic> json) =
|
||||
_$PortionCommentImpl.fromJson;
|
||||
|
||||
@override
|
||||
@JsonKey(name: 'id')
|
||||
int get portionId;
|
||||
@override
|
||||
String get comment;
|
||||
|
||||
/// Create a copy of PortionComment
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$PortionCommentImplCopyWith<_$PortionCommentImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
71
lib/features/diary/models/consultant_review_model.g.dart
Normal file
71
lib/features/diary/models/consultant_review_model.g.dart
Normal file
@@ -0,0 +1,71 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'consultant_review_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$ConsultantReviewModelImpl _$$ConsultantReviewModelImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$ConsultantReviewModelImpl(
|
||||
planName: json['planName'] as String,
|
||||
planDuration: (json['planDuration'] as num).toInt(),
|
||||
currentPlanDay: (json['currentPlanDay'] as num).toInt(),
|
||||
consultantComments: ConsultantComments.fromJson(
|
||||
json['consultantComments'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ConsultantReviewModelImplToJson(
|
||||
_$ConsultantReviewModelImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'planName': instance.planName,
|
||||
'planDuration': instance.planDuration,
|
||||
'currentPlanDay': instance.currentPlanDay,
|
||||
'consultantComments': instance.consultantComments,
|
||||
};
|
||||
|
||||
_$ConsultantCommentsImpl _$$ConsultantCommentsImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$ConsultantCommentsImpl(
|
||||
dayComment: json['dayComment'] as String?,
|
||||
mealsComments: (json['mealsComments'] as List<dynamic>)
|
||||
.map((e) => MealComment.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
portionsComments: (json['portionsComments'] as List<dynamic>)
|
||||
.map((e) => PortionComment.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ConsultantCommentsImplToJson(
|
||||
_$ConsultantCommentsImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'dayComment': instance.dayComment,
|
||||
'mealsComments': instance.mealsComments,
|
||||
'portionsComments': instance.portionsComments,
|
||||
};
|
||||
|
||||
_$MealCommentImpl _$$MealCommentImplFromJson(Map<String, dynamic> json) =>
|
||||
_$MealCommentImpl(
|
||||
comment: json['comment'] as String,
|
||||
typeNo: (json['typeNo'] as num).toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$MealCommentImplToJson(_$MealCommentImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'comment': instance.comment,
|
||||
'typeNo': instance.typeNo,
|
||||
};
|
||||
|
||||
_$PortionCommentImpl _$$PortionCommentImplFromJson(Map<String, dynamic> json) =>
|
||||
_$PortionCommentImpl(
|
||||
portionId: (json['id'] as num).toInt(),
|
||||
comment: json['comment'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$PortionCommentImplToJson(
|
||||
_$PortionCommentImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.portionId,
|
||||
'comment': instance.comment,
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
|
||||
@@ -6,7 +7,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class MealButtonWidget extends StatefulWidget {
|
||||
final int mealNumber;
|
||||
|
||||
const MealButtonWidget({
|
||||
super.key,
|
||||
required this.mealNumber,
|
||||
@@ -22,6 +22,18 @@ class _MealButtonWidgetState extends State<MealButtonWidget> {
|
||||
return BlocBuilder<DiaryCubit, DiaryState>(
|
||||
builder: (context, state) {
|
||||
if (state is DiaryStateLoaded) {
|
||||
final consultantComment = state.consultantReview?.consultantComments.mealsComments.firstWhereOrNull(
|
||||
(element) => element.typeNo == widget.mealNumber,
|
||||
);
|
||||
|
||||
final Color bgColor;
|
||||
if (state.selectedMeal == widget.mealNumber) {
|
||||
bgColor = appTheme.blueA400;
|
||||
} else if (consultantComment != null) {
|
||||
bgColor = appTheme.commentBg;
|
||||
} else {
|
||||
bgColor = const Color(0xFFF1F2F6);
|
||||
}
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
context.read<DiaryCubit>().setSelectedMeal(widget.mealNumber);
|
||||
@@ -32,7 +44,7 @@ class _MealButtonWidgetState extends State<MealButtonWidget> {
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
color: state.selectedMeal == widget.mealNumber ? appTheme.blueA400 : const Color(0xFFF1F2F6),
|
||||
color: bgColor,
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
|
||||
@@ -81,8 +82,10 @@ class _MealWidgetState extends State<MealWidget> {
|
||||
BlocBuilder<DiaryCubit, DiaryState>(
|
||||
builder: (context, state) {
|
||||
if (state is DiaryStateLoaded) {
|
||||
final mealsComments = state.consultantReview?.consultantComments.mealsComments;
|
||||
if (mealsComments != null && (state.selectedMeal - 1 < mealsComments.length)) {
|
||||
final consultantCommentOfMeal = state.consultantReview?.consultantComments.mealsComments.firstWhereOrNull(
|
||||
(element) => element.typeNo == state.selectedMeal,
|
||||
);
|
||||
if (consultantCommentOfMeal != null) {
|
||||
return Column(
|
||||
children: [
|
||||
Text(
|
||||
@@ -90,7 +93,7 @@ class _MealWidgetState extends State<MealWidget> {
|
||||
style: TextStyles.textLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
CommentContainer(comment: mealsComments[state.selectedMeal - 1].comment),
|
||||
CommentContainer(comment: consultantCommentOfMeal.comment),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import 'package:eatvio_app/features/glossary/models/glossary_model.dart';
|
||||
|
||||
class DiaryRepository {
|
||||
final DiaryService diaryService;
|
||||
final StreamController<String> _diaryForceFetchController = StreamController<String>();
|
||||
final StreamController<String> _diaryForceFetchController = StreamController<String>.broadcast();
|
||||
|
||||
Stream<String> get forceFetchStream => _diaryForceFetchController.stream;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:eatvio_app/core/models/pagination_model.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||
import 'package:eatvio_app/features/my_finance/models/card_model.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'plan_pagination_model.freezed.dart';
|
||||
@@ -70,10 +71,10 @@ class PacketOptions with _$PacketOptions {
|
||||
class PlanType with _$PlanType {
|
||||
const PlanType._();
|
||||
const factory PlanType({
|
||||
@JsonKey(name: "fit") required bool? fit,
|
||||
@JsonKey(name: "slim") required bool? slim,
|
||||
@JsonKey(name: "behavior") required bool? behavior,
|
||||
@JsonKey(name: "wellness") required bool? wellness,
|
||||
@JsonKey(name: "fit", fromJson: parseBool) required bool? fit,
|
||||
@JsonKey(name: "slim", fromJson: parseBool) required bool? slim,
|
||||
@JsonKey(name: "behavior", fromJson: parseBool) required bool? behavior,
|
||||
@JsonKey(name: "wellness", fromJson: parseBool) required bool? wellness,
|
||||
}) = _PlanType;
|
||||
|
||||
factory PlanType.fromJson(Map<String, dynamic> json) => _$PlanTypeFromJson(json);
|
||||
|
||||
@@ -118,7 +118,7 @@ class _MyDietWidgetState extends State<MyDietWidget> {
|
||||
if (!context.mounted) return;
|
||||
showBottomSheetWithBlur(
|
||||
context: context,
|
||||
child: SuccessPublishedDietBottomSheet(title: widget.diet.name),
|
||||
child: SuccessPublishedDietOrPlanBottomSheet(title: widget.diet.name),
|
||||
);
|
||||
await context.read<MyDietsCubit>().loadAllDiets();
|
||||
} else {
|
||||
|
||||
@@ -5,7 +5,6 @@ part 'card_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class CardModel with _$CardModel {
|
||||
|
||||
factory CardModel({
|
||||
required int id,
|
||||
@JsonKey(name: 'bank_pan') required String cardNumber,
|
||||
@@ -16,7 +15,7 @@ class CardModel with _$CardModel {
|
||||
}
|
||||
|
||||
bool parseBool(json) {
|
||||
if (json == 1 || json == true) {
|
||||
if (json == 1 || json == true || json == '1') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -28,20 +28,19 @@ class UserCardsCubit extends Cubit<UserCardsState> {
|
||||
emit(state.copyWith(cards: cards, state: BaseState.success));
|
||||
} catch (e, st) {
|
||||
log(e.toString(), stackTrace: st);
|
||||
emit(state.copyWith(state: BaseState.error, error: e.toString()));
|
||||
emit(state.copyWith(state: BaseState.error, error: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> markAsDefault({required int id}) async {
|
||||
final oldCards = [...state.cards];
|
||||
if (isClosed) return;
|
||||
final cards =
|
||||
oldCards.map((card) {
|
||||
if (card.id == id) {
|
||||
return card.copyWith(isDefault: true);
|
||||
}
|
||||
return card.copyWith(isDefault: false);
|
||||
}).toList();
|
||||
final cards = oldCards.map((card) {
|
||||
if (card.id == id) {
|
||||
return card.copyWith(isDefault: true);
|
||||
}
|
||||
return card.copyWith(isDefault: false);
|
||||
}).toList();
|
||||
emit(state.copyWith(cards: cards));
|
||||
|
||||
EasyDebounce.debounce('markAsDefault', Duration(milliseconds: id), () async {
|
||||
@@ -66,4 +65,14 @@ class UserCardsCubit extends Cubit<UserCardsState> {
|
||||
log(e.toString(), stackTrace: st);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> collectCharge({required int chosenPlaId}) async {
|
||||
try {
|
||||
await cardsRepository.collectCharge(chosenPlaId: chosenPlaId);
|
||||
return true;
|
||||
} catch (e, st) {
|
||||
log(e.toString(), stackTrace: st);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import 'package:eatvio_app/core/app_router.dart';
|
||||
import 'package:eatvio_app/core/enums/base_state.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart';
|
||||
import 'package:eatvio_app/core/widgets/bottom_sheet_with_blur.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
|
||||
import 'package:eatvio_app/core/widgets/data_error_widget.dart';
|
||||
import 'package:eatvio_app/core/widgets/loader.dart';
|
||||
import 'package:eatvio_app/features/my_finance/presentation/cubit/user_cards_cubit/user_cards_cubit.dart';
|
||||
@@ -11,104 +15,221 @@ import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class CardSettingsScreen extends StatelessWidget {
|
||||
static const path = '/card-settings-screen';
|
||||
static const name = 'cardSettingsScreen';
|
||||
const CardSettingsScreen({super.key});
|
||||
|
||||
const CardSettingsScreen({super.key, this.chosenPlanId});
|
||||
final int? chosenPlanId;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(create: (context) => sl<UserCardsCubit>()..getCards(), child: const CardsSettingsContent());
|
||||
return BlocProvider(
|
||||
create: (context) => sl<UserCardsCubit>()..getCards(),
|
||||
child: CardsSettingsContent(
|
||||
chosenPlanId: chosenPlanId,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
class CardsSettingsContent extends StatelessWidget {
|
||||
const CardsSettingsContent({super.key});
|
||||
const CardsSettingsContent({super.key, this.chosenPlanId});
|
||||
final int? chosenPlanId;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = S.of(context);
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF0F2F5),
|
||||
appBar: AppBar(
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(bottom: Radius.circular(12))),
|
||||
title: Text(s.my_cards),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(12), color: Colors.white),
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text('Привязанные карты', style: TextStyle(color: Color(0xFF323F4B), fontSize: 18, fontWeight: FontWeight.w600, height: 1)),
|
||||
return BlocBuilder<UserCardsCubit, UserCardsState>(
|
||||
builder: (context, state) {
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF0F2F5),
|
||||
appBar: AppBar(
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(bottom: Radius.circular(12))),
|
||||
title: Text(s.my_cards),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(12), color: Colors.white),
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text('Привязанные карты', style: TextStyle(color: Color(0xFF323F4B), fontSize: 18, fontWeight: FontWeight.w600, height: 1)),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final defaultCardNumber = state.cards.firstWhereOrNull((element) => element.isDefault)?.cardNumber;
|
||||
if (defaultCardNumber != null) {
|
||||
return Text(
|
||||
'Выплату будут производиться\nна карту ${defaultCardNumber.substring(defaultCardNumber.length - 7)}',
|
||||
style: const TextStyle(color: Color(0xFF323F4B), fontSize: 16, fontWeight: FontWeight.w500, height: 1),
|
||||
);
|
||||
}
|
||||
return Text(s.no_default_cards, style: TextStyles.textMMedium.copyWith(color: appTheme.black));
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
switch (state.state) {
|
||||
case BaseState.error:
|
||||
return SizedBox(
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
child: Center(
|
||||
child: DataErrorWidget(
|
||||
onReload: () async {
|
||||
context.read<UserCardsCubit>().getCards();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
case BaseState.success:
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 12),
|
||||
child: Row(
|
||||
spacing: 12,
|
||||
children: List.generate(state.cards.length + 1, (index) {
|
||||
if (index < state.cards.length) {
|
||||
final cardsNumber = state.cards[index].cardNumber.substring(state.cards[index].cardNumber.length - 7);
|
||||
return CardWidget(isSelected: state.cards[index].isDefault, cardId: state.cards[index].id, cardNumber: cardsNumber);
|
||||
}
|
||||
return const CardWidget(enabledAddCard: true);
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
default:
|
||||
return SizedBox(width: MediaQuery.sizeOf(context).width, height: 200, child: const Center(child: Loader()));
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
),
|
||||
if (chosenPlanId != null && state.cards.isNotEmpty) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: BlocBuilder<UserCardsCubit, UserCardsState>(
|
||||
builder: (context, state) {
|
||||
child: CustomOutlinedButton(
|
||||
text: s.collectCharge,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.green)),
|
||||
onPressed: () async {
|
||||
final defaultCardNumber = state.cards.firstWhereOrNull((element) => element.isDefault)?.cardNumber;
|
||||
if (defaultCardNumber != null) {
|
||||
return Text(
|
||||
'Выплату будут производиться\nна карту ${defaultCardNumber.substring(defaultCardNumber.length - 7)}',
|
||||
style: const TextStyle(color: Color(0xFF323F4B), fontSize: 16, fontWeight: FontWeight.w500, height: 1),
|
||||
final result = await context.read<UserCardsCubit>().collectCharge(chosenPlaId: chosenPlanId!);
|
||||
if (context.mounted) {
|
||||
if (!result) {
|
||||
showBottomSheetWithBlur(
|
||||
context: context,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
const BottomSheetHandlerBar(),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(
|
||||
s.chargeCollectionFailed,
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Text(
|
||||
s.contactSupport,
|
||||
style: TextStyles.textMMediumAuto.copyWith(color: appTheme.black),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: CustomOutlinedButton(
|
||||
onPressed: () {
|
||||
AppRouter.navigatorKey.currentContext?.pop();
|
||||
},
|
||||
text: s.ok,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textSSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: const WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
showBottomSheetWithBlur(
|
||||
context: context,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
const BottomSheetHandlerBar(),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(
|
||||
s.chargeApproved,
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Text(
|
||||
'${s.moneySentToCard} ${defaultCardNumber?.substring(defaultCardNumber.length - 7)}',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyles.textMMediumAuto.copyWith(color: appTheme.black),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: CustomOutlinedButton(
|
||||
onPressed: () {
|
||||
AppRouter.navigatorKey.currentContext?.pop();
|
||||
},
|
||||
text: s.ok,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textSSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: const WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
return Text(s.no_default_cards, style: TextStyles.textMMedium.copyWith(color: appTheme.black));
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: BlocBuilder<UserCardsCubit, UserCardsState>(
|
||||
builder: (context, state) {
|
||||
switch (state.state) {
|
||||
case BaseState.error:
|
||||
return SizedBox(
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
child: Center(
|
||||
child: DataErrorWidget(
|
||||
onReload: () async {
|
||||
context.read<UserCardsCubit>().getCards();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
case BaseState.success:
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 12),
|
||||
child: Row(
|
||||
spacing: 12,
|
||||
children: List.generate(state.cards.length + 1, (index) {
|
||||
if (index < state.cards.length) {
|
||||
final cardsNumber = state.cards[index].cardNumber.substring(state.cards[index].cardNumber.length - 7);
|
||||
return CardWidget(isSelected: state.cards[index].isDefault, cardId: state.cards[index].id, cardNumber: cardsNumber);
|
||||
}
|
||||
return const CardWidget(enabledAddCard: true);
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
default:
|
||||
return SizedBox(width: MediaQuery.sizeOf(context).width, height: 200, child: const Center(child: Loader()));
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
)
|
||||
]
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,4 +36,12 @@ class CardsRepository {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> collectCharge({required int chosenPlaId}) async {
|
||||
try {
|
||||
await cardsService.collectCharge(chosenPlanId: chosenPlaId);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,5 +24,10 @@ abstract class CardsService {
|
||||
Future<void> deleteCard({
|
||||
@Path('id') required int id,
|
||||
});
|
||||
|
||||
@POST('/api/chosen_plans/{id}/take_payin')
|
||||
Future<void> collectCharge({
|
||||
@Path('id') required int chosenPlanId,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart'
|
||||
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
|
||||
import 'package:eatvio_app/features/order_info/cubit/order_info_cubit.dart';
|
||||
import 'package:eatvio_app/features/order_info/cubit/order_info_state.dart';
|
||||
import 'package:eatvio_app/features/order_info/presentation/widgets/collect_charge_banner.dart';
|
||||
import 'package:eatvio_app/features/order_info/presentation/widgets/payment_banner.dart';
|
||||
import 'package:eatvio_app/features/order_info/presentation/widgets/plan_info.dart';
|
||||
import 'package:eatvio_app/features/order_info/presentation/widgets/plan_props.dart';
|
||||
@@ -61,7 +62,7 @@ class _InfoByOrderScreenContentState extends State<InfoByOrderScreenContent> {
|
||||
builder: (context, state) {
|
||||
final plan = state.chosenPlan;
|
||||
if (state.isLoading) {
|
||||
Scaffold(
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
s.infoAboutOrder,
|
||||
@@ -113,7 +114,7 @@ class _InfoByOrderScreenContentState extends State<InfoByOrderScreenContent> {
|
||||
planStatus = PlanStatusValues.paid;
|
||||
} else if (widget.successfulPayment == false) {
|
||||
planStatus = PlanStatusValues.paymentFailed;
|
||||
} else if (plan.chosenPlanState == ChosenPlanStateEnum.statePaid) {
|
||||
} else if (plan.chosenPlanState.isStateAfterPaidState()) {
|
||||
planStatus = PlanStatusValues.paid;
|
||||
} else {
|
||||
planStatus = PlanStatusValues.pendingPayment;
|
||||
@@ -134,6 +135,12 @@ class _InfoByOrderScreenContentState extends State<InfoByOrderScreenContent> {
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
if (plan.chosenPlanState == ChosenPlanStateEnum.stateDone) ...[
|
||||
CollectChargeBanner(
|
||||
chosenPlan: plan,
|
||||
price: chosenOption?.price ?? 0,
|
||||
)
|
||||
],
|
||||
if (widget.successfulPayment != null) ...[
|
||||
const SizedBox(height: 8),
|
||||
PaymentBanner(
|
||||
@@ -148,7 +155,10 @@ class _InfoByOrderScreenContentState extends State<InfoByOrderScreenContent> {
|
||||
backgroundColor: appTheme.whiteA700,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
PlanProps(planDuration: plan.plan.duration, planPrice: chosenOption?.price.toDouble() ?? 0, planStatus: planStatus),
|
||||
PlanProps(
|
||||
planDuration: plan.plan.duration,
|
||||
planPrice: chosenOption?.price ?? 0,
|
||||
planStatus: planStatus),
|
||||
const SizedBox(height: 8),
|
||||
PlanInfo(
|
||||
title: plan.plan.name,
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
|
||||
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
|
||||
import 'package:eatvio_app/features/my_finance/presentation/pages/card_settings.dart';
|
||||
import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class CollectChargeBanner extends StatelessWidget {
|
||||
const CollectChargeBanner({super.key, required this.chosenPlan, required this.price});
|
||||
final ChosenPlanModel chosenPlan;
|
||||
final double price;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = S.of(context);
|
||||
return MainBackgroundBouble(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
child: Container(
|
||||
width: 4,
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.blueA400, borderRadius: const BorderRadius.only(topRight: Radius.circular(20), bottomRight: Radius.circular(20))),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 80),
|
||||
child: Text(
|
||||
'${s.congratsYouHaveFinishedPlan} «${chosenPlan.plan.name}»',
|
||||
style: TextStyles.textMSemibold.copyWith(
|
||||
color: appTheme.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 80),
|
||||
child: Text(
|
||||
'${s.userConfirmedPlanCompletion} $price₽',
|
||||
style: TextStyles.textSMedium.copyWith(
|
||||
color: appTheme.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
CustomOutlinedButton(
|
||||
text: s.collectCharge,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.green)),
|
||||
onPressed: () async {
|
||||
GoRouter.of(context).push(CardSettingsScreen.path, extra: chosenPlan.id);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ class PaymentBanner extends StatefulWidget {
|
||||
const PaymentBanner({
|
||||
super.key,
|
||||
required this.successfulPayment,
|
||||
|
||||
});
|
||||
final bool successfulPayment;
|
||||
|
||||
|
||||
@@ -237,6 +237,7 @@ class _AboutPlanScreenState extends State<AboutPlanScreen> {
|
||||
'chatScreen',
|
||||
extra: {
|
||||
'chatId': chosenPlan.chatId,
|
||||
'sendInitialMessage': true,
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
@@ -33,6 +33,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
),
|
||||
"active_diet_state": MessageLookupByLibrary.simpleMessage("Активная"),
|
||||
"add": MessageLookupByLibrary.simpleMessage("Добавить"),
|
||||
"addAtLeastOneItem": MessageLookupByLibrary.simpleMessage(
|
||||
"Добавьте хотя бы одину опцию",
|
||||
),
|
||||
"add_card": MessageLookupByLibrary.simpleMessage("Привязать карту"),
|
||||
"add_cover": MessageLookupByLibrary.simpleMessage("Добавьте обложку"),
|
||||
"add_measure": MessageLookupByLibrary.simpleMessage("Добавить замер"),
|
||||
@@ -55,6 +58,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"another_form_of_stage": MessageLookupByLibrary.simpleMessage("этапа"),
|
||||
"answer": MessageLookupByLibrary.simpleMessage("Ответить"),
|
||||
"archived": MessageLookupByLibrary.simpleMessage("В архиве"),
|
||||
"atLeast3Symbols": MessageLookupByLibrary.simpleMessage(
|
||||
"Минимум 3 символа",
|
||||
),
|
||||
"authentificate": MessageLookupByLibrary.simpleMessage("Войти"),
|
||||
"average_pressure": MessageLookupByLibrary.simpleMessage(
|
||||
"Средние показатели давления:",
|
||||
@@ -86,6 +92,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.",
|
||||
),
|
||||
"carbohydrates": MessageLookupByLibrary.simpleMessage("Углеводы"),
|
||||
"chargeApproved": MessageLookupByLibrary.simpleMessage("Выплата одобрена"),
|
||||
"chargeCollectionFailed": MessageLookupByLibrary.simpleMessage(
|
||||
"Не удалось оформить выплату",
|
||||
),
|
||||
"chat": MessageLookupByLibrary.simpleMessage("Чат"),
|
||||
"check_out_video_recipe": MessageLookupByLibrary.simpleMessage(
|
||||
"Посмотреть видео рецепт",
|
||||
@@ -105,6 +115,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"choose_start_diet_date": MessageLookupByLibrary.simpleMessage(
|
||||
"Выберите дату старты диеты. Она отобразиться у Вас в дневнике",
|
||||
),
|
||||
"chosePlanType": MessageLookupByLibrary.simpleMessage("Выберите тип плана"),
|
||||
"chose_plan_start_date_2": MessageLookupByLibrary.simpleMessage(
|
||||
"Выберите дату старты плана. Она отобразиться в дневнике пользователя",
|
||||
),
|
||||
@@ -119,6 +130,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
),
|
||||
"city": MessageLookupByLibrary.simpleMessage("Город"),
|
||||
"city_of_living": MessageLookupByLibrary.simpleMessage("Город проживания"),
|
||||
"collectCharge": MessageLookupByLibrary.simpleMessage("Получить выплату"),
|
||||
"comfort": MessageLookupByLibrary.simpleMessage("Комфорт"),
|
||||
"comment": MessageLookupByLibrary.simpleMessage("комментарий"),
|
||||
"commentForMeal": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -138,9 +150,15 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Ваше обращение принято! Мы рассмотрим его в ближайшее время и примем необходимые меры. Спасибо за вашу бдительность и помощь в улучшении нашего сервиса.",
|
||||
),
|
||||
"confirm": MessageLookupByLibrary.simpleMessage("Подтвердить"),
|
||||
"congratsYouHaveFinishedPlan": MessageLookupByLibrary.simpleMessage(
|
||||
"Поздравляем, Вы закончили план",
|
||||
),
|
||||
"consultations_count": MessageLookupByLibrary.simpleMessage(
|
||||
"Кол-во проведенных консулдьтаций",
|
||||
),
|
||||
"contactSupport": MessageLookupByLibrary.simpleMessage(
|
||||
"Обратитесь в поддержку",
|
||||
),
|
||||
"continueButton": MessageLookupByLibrary.simpleMessage("Продолжить"),
|
||||
"continue_": MessageLookupByLibrary.simpleMessage("Продолжить"),
|
||||
"copied": MessageLookupByLibrary.simpleMessage("cкопирован"),
|
||||
@@ -385,6 +403,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Диета с мясом полезная и сбалансированная. Включает в себя нежирное мясо: курицу, индейку, кролика или говядину. В сочетании с овощами получаете все необходимые витамины и минералы. Не забывайте о порциях и старайтесь есть медленно, прислушиваясь к своему организму.",
|
||||
),
|
||||
"milliliter": MessageLookupByLibrary.simpleMessage("мл"),
|
||||
"minPlanPrice": MessageLookupByLibrary.simpleMessage(
|
||||
"Минимальная сумма 1200₽",
|
||||
),
|
||||
"minimal_customer_price": MessageLookupByLibrary.simpleMessage(
|
||||
"Минимальная цена для покупателя: 1200₽",
|
||||
),
|
||||
@@ -396,6 +417,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"misrepresentation_description": MessageLookupByLibrary.simpleMessage(
|
||||
"Новости, которые могут вызвать широкий резонанс или несут угрозу, распространяемые с целью введения людей в заблуждение.",
|
||||
),
|
||||
"moneySentToCard": MessageLookupByLibrary.simpleMessage(
|
||||
"Средства отправлены на карту",
|
||||
),
|
||||
"more_about_order": MessageLookupByLibrary.simpleMessage(
|
||||
"Подробнее о заказе",
|
||||
),
|
||||
@@ -631,6 +655,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Верхние показатели давления:",
|
||||
),
|
||||
"upper": MessageLookupByLibrary.simpleMessage("Верхнее"),
|
||||
"userConfirmedPlanCompletion": MessageLookupByLibrary.simpleMessage(
|
||||
"Пользователь подтвердил прохождения плана. В ближайшее время Вы получите",
|
||||
),
|
||||
"user_authorization": MessageLookupByLibrary.simpleMessage(
|
||||
"Аутентификация",
|
||||
),
|
||||
|
||||
@@ -3074,6 +3074,116 @@ class S {
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Поздравляем, Вы закончили план`
|
||||
String get congratsYouHaveFinishedPlan {
|
||||
return Intl.message(
|
||||
'Поздравляем, Вы закончили план',
|
||||
name: 'congratsYouHaveFinishedPlan',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Пользователь подтвердил прохождения плана. В ближайшее время Вы получите`
|
||||
String get userConfirmedPlanCompletion {
|
||||
return Intl.message(
|
||||
'Пользователь подтвердил прохождения плана. В ближайшее время Вы получите',
|
||||
name: 'userConfirmedPlanCompletion',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Получить выплату`
|
||||
String get collectCharge {
|
||||
return Intl.message(
|
||||
'Получить выплату',
|
||||
name: 'collectCharge',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Выплата одобрена`
|
||||
String get chargeApproved {
|
||||
return Intl.message(
|
||||
'Выплата одобрена',
|
||||
name: 'chargeApproved',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Средства отправлены на карту`
|
||||
String get moneySentToCard {
|
||||
return Intl.message(
|
||||
'Средства отправлены на карту',
|
||||
name: 'moneySentToCard',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Не удалось оформить выплату`
|
||||
String get chargeCollectionFailed {
|
||||
return Intl.message(
|
||||
'Не удалось оформить выплату',
|
||||
name: 'chargeCollectionFailed',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Обратитесь в поддержку`
|
||||
String get contactSupport {
|
||||
return Intl.message(
|
||||
'Обратитесь в поддержку',
|
||||
name: 'contactSupport',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Минимум 3 символа`
|
||||
String get atLeast3Symbols {
|
||||
return Intl.message(
|
||||
'Минимум 3 символа',
|
||||
name: 'atLeast3Symbols',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Выберите тип плана`
|
||||
String get chosePlanType {
|
||||
return Intl.message(
|
||||
'Выберите тип плана',
|
||||
name: 'chosePlanType',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Минимальная сумма 1200₽`
|
||||
String get minPlanPrice {
|
||||
return Intl.message(
|
||||
'Минимальная сумма 1200₽',
|
||||
name: 'minPlanPrice',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Добавьте хотя бы одину опцию`
|
||||
String get addAtLeastOneItem {
|
||||
return Intl.message(
|
||||
'Добавьте хотя бы одину опцию',
|
||||
name: 'addAtLeastOneItem',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
|
||||
|
||||
@@ -370,6 +370,17 @@
|
||||
"orderCanceled": "Заказ отменен",
|
||||
"couldNotCancelOrder": "Не удалось отменить заказ",
|
||||
"dayComment": "Комментарий ко всему дню",
|
||||
"mealComment": "Комментарий к приему пищи"
|
||||
"mealComment": "Комментарий к приему пищи",
|
||||
"congratsYouHaveFinishedPlan": "Поздравляем, Вы закончили план",
|
||||
"userConfirmedPlanCompletion": "Пользователь подтвердил прохождения плана. В ближайшее время Вы получите",
|
||||
"collectCharge": "Получить выплату",
|
||||
"chargeApproved": "Выплата одобрена",
|
||||
"moneySentToCard": "Средства отправлены на карту",
|
||||
"chargeCollectionFailed": "Не удалось оформить выплату",
|
||||
"contactSupport": "Обратитесь в поддержку",
|
||||
"atLeast3Symbols": "Минимум 3 символа",
|
||||
"chosePlanType": "Выберите тип плана",
|
||||
"minPlanPrice": "Минимальная сумма 1200₽",
|
||||
"addAtLeastOneItem": "Добавьте хотя бы одину опцию"
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user