feat(diary): consultant comments

This commit is contained in:
Username1548
2025-04-24 04:20:24 +07:00
parent 267cc38a0b
commit 3754d047fb
102 changed files with 6021 additions and 494 deletions

3
assets/icons/edit.svg Normal file
View File

@@ -0,0 +1,3 @@
<svg width="21" height="18" viewBox="0 0 21 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.125 11.3333C0.877083 11.3333 0.66932 11.2494 0.501709 11.0816C0.333903 10.9138 0.25 10.706 0.25 10.458C0.25 10.2099 0.333903 10.0022 0.501709 9.83475C0.66932 9.66714 0.877083 9.58333 1.125 9.58333H7.54167C7.78958 9.58333 7.99745 9.66724 8.16525 9.83504C8.33286 10.0028 8.41667 10.2107 8.41667 10.4586C8.41667 10.7067 8.33286 10.9145 8.16525 11.0819C7.99745 11.2495 7.78958 11.3333 7.54167 11.3333H1.125ZM1.125 6.66667C0.877083 6.66667 0.66932 6.58276 0.501709 6.41496C0.333903 6.24715 0.25 6.03929 0.25 5.79137C0.25 5.54326 0.333903 5.3355 0.501709 5.16808C0.66932 5.00047 0.877083 4.91667 1.125 4.91667H12.2083C12.4563 4.91667 12.6641 5.00057 12.8319 5.16838C12.9995 5.33618 13.0833 5.54404 13.0833 5.79196C13.0833 6.04007 12.9995 6.24783 12.8319 6.41525C12.6641 6.58286 12.4563 6.66667 12.2083 6.66667H1.125ZM1.125 2C0.877083 2 0.66932 1.9161 0.501709 1.74829C0.333903 1.58049 0.25 1.37262 0.25 1.12471C0.25 0.876597 0.333903 0.668833 0.501709 0.501417C0.66932 0.333805 0.877083 0.25 1.125 0.25H12.2083C12.4563 0.25 12.6641 0.333903 12.8319 0.501708C12.9995 0.669514 13.0833 0.877375 13.0833 1.12529C13.0833 1.3734 12.9995 1.58117 12.8319 1.74858C12.6641 1.91619 12.4563 2 12.2083 2H1.125ZM10.3014 16.6953V15.1069C10.3014 14.9696 10.3268 14.8366 10.3775 14.7079C10.4285 14.5792 10.5085 14.4602 10.6176 14.3509L16.635 8.36271C16.78 8.21765 16.9366 8.11557 17.1048 8.05646C17.273 7.99754 17.4412 7.96808 17.6094 7.96808C17.7928 7.96808 17.9702 8.0024 18.1417 8.07104C18.3132 8.13987 18.4691 8.24312 18.6093 8.38079L19.6884 9.47104C19.814 9.61629 19.9113 9.7734 19.9801 9.94238C20.0489 10.1113 20.0833 10.2803 20.0833 10.4493C20.0833 10.6183 20.0519 10.7902 19.9891 10.9653C19.9263 11.1403 19.8261 11.3003 19.6884 11.4453L13.7002 17.4335C13.5911 17.5428 13.4722 17.6228 13.3435 17.6736C13.2146 17.7245 13.0815 17.75 12.9442 17.75H11.3558C11.0569 17.75 10.8065 17.6489 10.6045 17.4467C10.4024 17.2446 10.3014 16.9942 10.3014 16.6953ZM11.6924 16.359H12.8007L16.5877 12.5537L16.0516 11.9995L15.5085 11.4523L11.6924 15.2507V16.359ZM16.0516 11.9995L15.5085 11.4523L16.5877 12.5537L16.0516 11.9995Z" fill="#809CB9"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -33,7 +33,7 @@ import 'package:eatvio_app/features/my_finance/presentation/pages/card_settings.
import 'package:eatvio_app/features/my_finance/presentation/pages/consultant_finance_screen.dart';
import 'package:eatvio_app/features/my_finance/presentation/pages/user_finance_screen.dart';
import 'package:eatvio_app/features/my_plans/presentation/my_plans_screen.dart';
import 'package:eatvio_app/features/notifications/presentation/pages/notifications_screen.dart';
import 'package:eatvio_app/features/notifications/presentation/notifications_screen.dart';
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
import 'package:eatvio_app/features/plans/presentation/pages/about_plan_screen.dart';
import 'package:eatvio_app/features/profile/presentation/pages/profile_screen.dart';
@@ -99,8 +99,8 @@ class AppRouter {
GoRoute(path: '/feed-screen', name: 'feedScreen', pageBuilder: (context, state) => const NoTransitionPage(child: FeedScreen())),
GoRoute(path: '/diary-screen', name: 'diaryScreen', pageBuilder: (context, state) => const NoTransitionPage(child: DiaryScreen())),
GoRoute(
path: '/consultations-screen',
name: 'consultationsScreen',
path: ConsultationsScreen.path,
name: ConsultationsScreen.name,
pageBuilder: (context, state) => const NoTransitionPage(child: ConsultationsScreen())),
GoRoute(path: '/chat-select-screen', name: 'chatSelectScreen', pageBuilder: (context, state) => const NoTransitionPage(child: ChatsScreen())),
GoRoute(
@@ -256,29 +256,41 @@ class AppRouter {
GoRoute(path: DevToolsScreen.path, name: DevToolsScreen.name, builder: (context, state) => const DevToolsScreen()),
GoRoute(path: ConsultantRatingScreen.path, name: ConsultantRatingScreen.name, builder: (context, state) => const ConsultantRatingScreen()),
GoRoute(
path: InfoByOrderScreen.path,
name: InfoByOrderScreen.name,
builder: (context, state) {
final data = state.extra as Map<String, dynamic>;
final planId = data['planId'] as int;
final successfulPayment = data['successfulPayment'] as bool?;
return InfoByOrderScreen(
planId: planId,
successfulPayment: successfulPayment,
);
}),
path: InfoByOrderScreen.path,
name: InfoByOrderScreen.name,
builder: (context, state) {
final data = state.extra as Map<String, dynamic>;
final planId = data['planId'] as int;
final successfulPayment = data['successfulPayment'] as bool?;
return InfoByOrderScreen(
planId: planId,
successfulPayment: successfulPayment,
);
},
),
GoRoute(
path: PaymentScreen.path,
name: PaymentScreen.name,
builder: (context, state) {
final data = state.extra as Map<String, dynamic>;
final planId = data['planId'] as int;
final url = data['url'] as String;
return PaymentScreen(
url: url,
planId: planId,
);
}),
path: PaymentScreen.path,
name: PaymentScreen.name,
builder: (context, state) {
final data = state.extra as Map<String, dynamic>;
final planId = data['planId'] as int;
final url = data['url'] as String;
return PaymentScreen(
url: url,
planId: planId,
);
},
),
GoRoute(
path: UserDiaryScreen.path,
name: UserDiaryScreen.name,
builder: (context, state) {
final diaryId = state.extra as int;
return UserDiaryScreen(
diaryId: diaryId,
);
},
),
],
);
}

View File

@@ -1,5 +1,5 @@
String baseUrl = 'https://eatvio.ru';
// String baseUrl = 'https://dev2.eatvio.com';
// String baseUrl = 'https://eatvio.ru';
String baseUrl = 'https://dev2.eatvio.com';
const List<int> durationValues = [7, 14, 21];

View File

@@ -6,20 +6,27 @@ import 'package:eatvio_app/core/websockets/ws_service.dart';
import 'package:eatvio_app/features/auth/presentation/bloc/auth_cubit.dart';
import 'package:eatvio_app/features/auth/repositories/auth_repository.dart';
import 'package:eatvio_app/features/auth/services/auth_service.dart';
import 'package:eatvio_app/features/cancel_plan_bottom_sheet/cubit/cancel_plan_cubit.dart';
import 'package:eatvio_app/features/cancel_plan_bottom_sheet/repository/cancel_plan_repository.dart';
import 'package:eatvio_app/features/cancel_plan_bottom_sheet/service/cancel_plan_service.dart';
import 'package:eatvio_app/features/complaining/presentation/cubit/complaining_cubit.dart';
import 'package:eatvio_app/features/complaining/repositories/complaining_repository.dart';
import 'package:eatvio_app/features/complaining/services/complaining_service.dart';
import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_cubit.dart';
import 'package:eatvio_app/features/consultations/repositories/consultations_repository.dart';
import 'package:eatvio_app/features/consultations/services/consultation_service.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
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/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';
import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_cubit.dart';
import 'package:eatvio_app/features/blog_post_create/repositories/blog_post_repository.dart';
import 'package:eatvio_app/features/blog_post_create/services/blog_post_service.dart';
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/new_messages_cubit/new_messages_cubit.dart';
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/notifications_cubit/notifications_cubit.dart';
import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart';
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart';
import 'package:eatvio_app/features/calendar/presentation/bloc/calendar_cubit.dart';
import 'package:eatvio_app/features/calendar/repositories/calendar_repository.dart';
@@ -123,6 +130,9 @@ Future<void> init() async {
sl.registerFactory<ComplainingCubit>(() => ComplainingCubit(repository: sl()));
sl.registerFactory<UserCardsCubit>(() => UserCardsCubit(cardsRepository: sl()));
sl.registerFactory<OrderInfoCubit>(() => OrderInfoCubit(chatRepository: sl()));
sl.registerFactory<ConsultationsCubit>(() => ConsultationsCubit(consultationsRepository: sl()));
sl.registerFactory<ConsultantCommentsCubit>(() => ConsultantCommentsCubit(diaryRepository: sl()));
sl.registerFactory<CancelPlanCubit>(() => CancelPlanCubit(cancelPlanRepository: sl()));
// Repository
sl.registerLazySingleton<ChatRepository>(() => ChatRepository(chatService: sl()));
@@ -146,6 +156,8 @@ Future<void> init() async {
sl.registerLazySingleton<ComplainingRepository>(() => ComplainingRepository(service: sl()));
sl.registerLazySingleton<AuthRepository>(() => AuthRepository(authService: sl()));
sl.registerLazySingleton<CardsRepository>(() => CardsRepository(cardsService: sl()));
sl.registerLazySingleton<ConsultationsRepository>(() => ConsultationsRepository(consultationService: sl()));
sl.registerLazySingleton<CancelPlanRepository>(() => CancelPlanRepository(cancelService: sl()));
// Services
sl.registerLazySingleton<ChatService>(() => ChatService(sl()));
@@ -170,6 +182,8 @@ Future<void> init() async {
sl.registerLazySingleton<ComplainingService>(() => ComplainingService(sl()));
sl.registerLazySingleton<AuthService>(() => AuthService(sl()));
sl.registerLazySingleton<CardsService>(() => CardsService(sl()));
sl.registerLazySingleton<ConsultationService>(() => ConsultationService(sl()));
sl.registerLazySingleton<CancelPlanService>(() => CancelPlanService(sl()));
// External
sl.registerLazySingleton(

View File

@@ -185,6 +185,7 @@ class PrimaryColors {
Color get red => const Color(0xFFCD4141);
Color get strongRed => const Color(0xFFD01717);
Color get lightRed => const Color(0XFFD01717).withValues(alpha: 0.1);
Color get transparentRed => const Color(0xFFCD4141).withValues(alpha: 0.75);
@@ -196,6 +197,10 @@ class PrimaryColors {
Color get blueGray => const Color(0xFF809CB9);
Color get dialogShadow => const Color.fromARGB(125, 0, 0, 0);
Color get darkGray => const Color(0XFFBBBCC0);
Color get commentBg => const Color(0XFFFFFDD8);
}
PrimaryColors get appTheme => ThemeHelper().themeColor();

View File

@@ -20,7 +20,7 @@ class WsManager {
Stream<ChannelReadEvent>? get newMessagesStream => _privateMessagesChannel?.bind('App\\Events\\ChatMessageUpdated');
// TODO: change name of event to a right one
Stream<ChannelReadEvent>? get newNotificationsStream => _privateNotificationChannel?.bind('App\\Events\\ChatMessageUpdated');
Stream<ChannelReadEvent>? get newNotificationsStream => _privateNotificationChannel?.bind('Illuminate\\Notifications\\Events\\BroadcastNotificationCreated');
Stream<ChannelReadEvent>? get directChatStream => _privateDirectChatChannel?.bind('App\\Events\\ChatMessageUpdated');

View File

@@ -1,25 +0,0 @@
import 'package:eatvio_app/core/websockets/ws_manager.dart';
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/notifications_cubit/notifications_state.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class NotificationsCubit extends Cubit<NotificationsState> {
int _notificationsCount = 0;
List<String> notifications = [];
final WsManager wsManager;
NotificationsCubit({required this.wsManager}) : super(const NotificationsState.empty()) {
wsManager.newNotificationsStream?.listen((event) {
incrementCounter(event.data);
});
}
incrementCounter(String data) {
_notificationsCount++;
notifications.add(data);
emit(
NotificationsState.loaded(
notifications: notifications,
notificationsCount: _notificationsCount,
),
);
}
}

View File

@@ -1,7 +1,7 @@
import 'package:badges/badges.dart' as badges;
import 'package:eatvio_app/core/utils/image_constant.dart';
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/notifications_cubit/notifications_cubit.dart';
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/notifications_cubit/notifications_state.dart';
import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart';
import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_state.dart';
import 'package:eatvio_app/features/profile/models/profile_model.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chats_cubit/chats_state.dart';

View File

@@ -0,0 +1,289 @@
import 'package:eatvio_app/core/app_router.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/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/features/cancel_plan_bottom_sheet/cubit/cancel_plan_cubit.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
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 CancelPlanBottomSheet extends StatefulWidget {
const CancelPlanBottomSheet({super.key, this.enableForm = false, required this.planId});
final bool enableForm;
final int planId;
@override
State<CancelPlanBottomSheet> createState() => _CancelPlanBottomSheetState();
}
class _CancelPlanBottomSheetState extends State<CancelPlanBottomSheet> {
final amountController = TextEditingController();
final reasonController = TextEditingController();
@override
void initState() {
super.initState();
amountController.addListener(() {
if (!amountController.text.endsWith('₽')) {
final text = amountController.text.replaceAll('₽', '');
amountController.text = '$text₽';
amountController.selection = TextSelection.collapsed(offset: text.length);
}
});
}
bool isPriceValid = true;
bool isReasonValid = true;
bool? cancelationResult;
bool isLoading = false;
@override
Widget build(BuildContext context) {
final s = S.of(context);
if (cancelationResult != null) {
return _ComplaintWasSendWidget(isSuccess: cancelationResult!);
}
return BlocProvider(
create: (context) => sl<CancelPlanCubit>(),
child: Builder(builder: (context) {
if (!widget.enableForm) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 8),
const BottomSheetHandlerBar(),
const SizedBox(height: 20),
Text(
s.orederCancelation,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
const SizedBox(height: 8),
Text(s.youShureYouWantToCancelOrder, style: TextStyles.textMMediumAuto.copyWith(color: appTheme.black)),
const SizedBox(height: 20),
isLoading
? const Center(child: Loader())
: CustomOutlinedButton(
text: s.cancel,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle:
ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.strongRed)),
onPressed: () async {
setState(() {
isLoading = true;
});
final result = await context.read<CancelPlanCubit>().cancelChosenPlan(id: widget.planId);
setState(() {
cancelationResult = result;
isLoading = false;
});
},
),
const SizedBox(height: 30),
],
),
);
}
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 8),
const Center(child: BottomSheetHandlerBar()),
const SizedBox(height: 20),
Center(
child: Text(
s.orederCancelation,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
),
const SizedBox(height: 16),
Text(s.explainResonOfCancelation, style: TextStyles.textMMediumAuto.copyWith(color: appTheme.black)),
const SizedBox(height: 4),
CustomTextFormField(
maxLines: 6,
minLines: 4,
hintText: s.reasonExample,
controller: reasonController,
hasError: !isReasonValid,
),
if (!isReasonValid) ...[const SizedBox(height: 4), const ValidationErrorWidget(error: 'Минимум 3 символа')],
const SizedBox(height: 12),
Text(s.typeAmoutOfReturn, style: TextStyles.textMMediumAuto.copyWith(color: appTheme.black)),
const SizedBox(height: 4),
CustomTextFormField(
hintText: '0 ₽',
controller: amountController,
keyboardType: TextInputType.number,
hasError: !isPriceValid,
),
if (!isPriceValid) ...[const SizedBox(height: 4), const ValidationErrorWidget(error: 'Минимальная сумма 500₽')],
const SizedBox(height: 8),
Row(
children: [
InkWell(
onTap: () {
amountController.text = '1000₽';
},
borderRadius: BorderRadius.circular(200),
child: Ink(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
decoration: BoxDecoration(
color: appTheme.lightGray0,
borderRadius: BorderRadius.circular(200),
),
child: Text('1000 ₽', style: TextStyles.textXSSemibold.copyWith(color: appTheme.black)),
),
),
const SizedBox(width: 8),
InkWell(
onTap: () {
amountController.text = '2000₽';
},
borderRadius: BorderRadius.circular(200),
child: Ink(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
decoration: BoxDecoration(
color: appTheme.lightGray0,
borderRadius: BorderRadius.circular(200),
),
child: Text('2000 ₽', style: TextStyles.textXSSemibold.copyWith(color: appTheme.black)),
),
)
],
),
const SizedBox(height: 20),
isLoading
? const Center(child: Loader())
: CustomOutlinedButton(
text: s.cancel,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(
side: const WidgetStatePropertyAll(BorderSide.none),
backgroundColor: WidgetStatePropertyAll(
appTheme.strongRed,
),
),
onPressed: () async {
bool isValid = true;
if (reasonController.text.length < 3) {
setState(() {
isReasonValid = false;
});
isValid = false;
} else {
setState(() {
isReasonValid = true;
});
}
final price = int.tryParse(amountController.text.replaceAll('₽', '').trim());
if (price == null || price < 500) {
setState(() {
isPriceValid = false;
});
isValid = false;
} else {
setState(() {
isPriceValid = true;
});
}
if (!isValid) {
return;
}
setState(() {
isLoading = true;
});
final result = await context.read<CancelPlanCubit>().cancelChosenPlan(
id: widget.planId,
reason: reasonController.text,
price: price,
);
setState(() {
cancelationResult = result;
isLoading = false;
});
},
),
const SizedBox(height: 30),
],
),
);
}),
);
}
}
class _ComplaintWasSendWidget extends StatelessWidget {
const _ComplaintWasSendWidget({required this.isSuccess});
final bool isSuccess;
@override
Widget build(BuildContext context) {
final s = S.of(context);
if (isSuccess) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 8),
const BottomSheetHandlerBar(),
const SizedBox(height: 20),
Text(
s.orderCanceled,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
const SizedBox(height: 20),
CustomOutlinedButton(
text: s.ok,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.strongRed)),
onPressed: () async {
AppRouter.navigatorKey.currentContext?.pop();
},
),
const SizedBox(height: 30),
],
),
);
}
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 8),
const BottomSheetHandlerBar(),
const SizedBox(height: 20),
Text(
s.couldNotCancelOrder,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
const SizedBox(height: 20),
CustomOutlinedButton(
text: s.ok,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.strongRed)),
onPressed: () async {
AppRouter.navigatorKey.currentContext?.pop();
},
),
const SizedBox(height: 30),
],
),
);
}
}

View File

@@ -0,0 +1,25 @@
import 'dart:developer';
import 'package:eatvio_app/features/cancel_plan_bottom_sheet/cubit/cancel_plan_state.dart';
import 'package:eatvio_app/features/cancel_plan_bottom_sheet/models/cancelation_model.dart';
import 'package:eatvio_app/features/cancel_plan_bottom_sheet/repository/cancel_plan_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class CancelPlanCubit extends Cubit<CancelPlanState> {
final CancelPlanRepository cancelPlanRepository;
CancelPlanCubit({required this.cancelPlanRepository}) : super(CancelPlanState());
Future<bool> cancelChosenPlan({required int id, String? reason, int? price}) async{
try {
CancelationModel? body;
if (reason != null && price != null) {
body = CancelationModel(description: reason, price: price);
}
await cancelPlanRepository.cancelChosenPlan(id: id, body: body);
return true;
} catch (e, st) {
log(e.toString(), stackTrace: st);
}
return false;
}
}

View File

@@ -0,0 +1,9 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'cancel_plan_state.freezed.dart';
@freezed
class CancelPlanState with _$CancelPlanState {
factory CancelPlanState() = _CancelPlanState;
}

View File

@@ -0,0 +1,82 @@
// 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 'cancel_plan_state.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');
/// @nodoc
mixin _$CancelPlanState {}
/// @nodoc
abstract class $CancelPlanStateCopyWith<$Res> {
factory $CancelPlanStateCopyWith(
CancelPlanState value, $Res Function(CancelPlanState) then) =
_$CancelPlanStateCopyWithImpl<$Res, CancelPlanState>;
}
/// @nodoc
class _$CancelPlanStateCopyWithImpl<$Res, $Val extends CancelPlanState>
implements $CancelPlanStateCopyWith<$Res> {
_$CancelPlanStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of CancelPlanState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$CancelPlanStateImplCopyWith<$Res> {
factory _$$CancelPlanStateImplCopyWith(_$CancelPlanStateImpl value,
$Res Function(_$CancelPlanStateImpl) then) =
__$$CancelPlanStateImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$CancelPlanStateImplCopyWithImpl<$Res>
extends _$CancelPlanStateCopyWithImpl<$Res, _$CancelPlanStateImpl>
implements _$$CancelPlanStateImplCopyWith<$Res> {
__$$CancelPlanStateImplCopyWithImpl(
_$CancelPlanStateImpl _value, $Res Function(_$CancelPlanStateImpl) _then)
: super(_value, _then);
/// Create a copy of CancelPlanState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$CancelPlanStateImpl implements _CancelPlanState {
_$CancelPlanStateImpl();
@override
String toString() {
return 'CancelPlanState()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$CancelPlanStateImpl);
}
@override
int get hashCode => runtimeType.hashCode;
}
abstract class _CancelPlanState implements CancelPlanState {
factory _CancelPlanState() = _$CancelPlanStateImpl;
}

View File

@@ -0,0 +1,14 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'cancelation_model.freezed.dart';
part 'cancelation_model.g.dart';
@freezed
class CancelationModel with _$CancelationModel {
factory CancelationModel({
required String description,
required int price,
}) = _CancelationModel;
factory CancelationModel.fromJson(Map<String, dynamic> json) => _$CancelationModelFromJson(json);
}

View File

@@ -0,0 +1,184 @@
// 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 'cancelation_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');
CancelationModel _$CancelationModelFromJson(Map<String, dynamic> json) {
return _CancelationModel.fromJson(json);
}
/// @nodoc
mixin _$CancelationModel {
String get description => throw _privateConstructorUsedError;
int get price => throw _privateConstructorUsedError;
/// Serializes this CancelationModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of CancelationModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$CancelationModelCopyWith<CancelationModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $CancelationModelCopyWith<$Res> {
factory $CancelationModelCopyWith(
CancelationModel value, $Res Function(CancelationModel) then) =
_$CancelationModelCopyWithImpl<$Res, CancelationModel>;
@useResult
$Res call({String description, int price});
}
/// @nodoc
class _$CancelationModelCopyWithImpl<$Res, $Val extends CancelationModel>
implements $CancelationModelCopyWith<$Res> {
_$CancelationModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of CancelationModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? description = null,
Object? price = null,
}) {
return _then(_value.copyWith(
description: null == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String,
price: null == price
? _value.price
: price // ignore: cast_nullable_to_non_nullable
as int,
) as $Val);
}
}
/// @nodoc
abstract class _$$CancelationModelImplCopyWith<$Res>
implements $CancelationModelCopyWith<$Res> {
factory _$$CancelationModelImplCopyWith(_$CancelationModelImpl value,
$Res Function(_$CancelationModelImpl) then) =
__$$CancelationModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String description, int price});
}
/// @nodoc
class __$$CancelationModelImplCopyWithImpl<$Res>
extends _$CancelationModelCopyWithImpl<$Res, _$CancelationModelImpl>
implements _$$CancelationModelImplCopyWith<$Res> {
__$$CancelationModelImplCopyWithImpl(_$CancelationModelImpl _value,
$Res Function(_$CancelationModelImpl) _then)
: super(_value, _then);
/// Create a copy of CancelationModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? description = null,
Object? price = null,
}) {
return _then(_$CancelationModelImpl(
description: null == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String,
price: null == price
? _value.price
: price // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// @nodoc
@JsonSerializable()
class _$CancelationModelImpl implements _CancelationModel {
_$CancelationModelImpl({required this.description, required this.price});
factory _$CancelationModelImpl.fromJson(Map<String, dynamic> json) =>
_$$CancelationModelImplFromJson(json);
@override
final String description;
@override
final int price;
@override
String toString() {
return 'CancelationModel(description: $description, price: $price)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$CancelationModelImpl &&
(identical(other.description, description) ||
other.description == description) &&
(identical(other.price, price) || other.price == price));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, description, price);
/// Create a copy of CancelationModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$CancelationModelImplCopyWith<_$CancelationModelImpl> get copyWith =>
__$$CancelationModelImplCopyWithImpl<_$CancelationModelImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$CancelationModelImplToJson(
this,
);
}
}
abstract class _CancelationModel implements CancelationModel {
factory _CancelationModel(
{required final String description,
required final int price}) = _$CancelationModelImpl;
factory _CancelationModel.fromJson(Map<String, dynamic> json) =
_$CancelationModelImpl.fromJson;
@override
String get description;
@override
int get price;
/// Create a copy of CancelationModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$CancelationModelImplCopyWith<_$CancelationModelImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -0,0 +1,21 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'cancelation_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$CancelationModelImpl _$$CancelationModelImplFromJson(
Map<String, dynamic> json) =>
_$CancelationModelImpl(
description: json['description'] as String,
price: (json['price'] as num).toInt(),
);
Map<String, dynamic> _$$CancelationModelImplToJson(
_$CancelationModelImpl instance) =>
<String, dynamic>{
'description': instance.description,
'price': instance.price,
};

View File

@@ -0,0 +1,17 @@
import 'package:eatvio_app/features/cancel_plan_bottom_sheet/models/cancelation_model.dart';
import 'package:eatvio_app/features/cancel_plan_bottom_sheet/service/cancel_plan_service.dart';
class CancelPlanRepository {
final CancelPlanService cancelService;
CancelPlanRepository({required this.cancelService});
Future<void> cancelChosenPlan({
required int id,
CancelationModel? body
}) async {
try {
await cancelService.cancelChosenPlan(id: id, body: body);
} catch (e) {
rethrow;
}
}
}

View File

@@ -0,0 +1,16 @@
import 'package:dio/dio.dart';
import 'package:eatvio_app/features/cancel_plan_bottom_sheet/models/cancelation_model.dart';
import 'package:retrofit/retrofit.dart';
part 'cancel_plan_service.g.dart';
@RestApi()
abstract class CancelPlanService {
factory CancelPlanService(Dio dio, {String baseUrl}) = _CancelPlanService;
@POST('/api/chosen_plans/{id}/cancel')
Future<void> cancelChosenPlan({
@Path() required int id,
@Body() CancelationModel? body
});
}

View File

@@ -0,0 +1,69 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'cancel_plan_service.dart';
// **************************************************************************
// RetrofitGenerator
// **************************************************************************
// ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers,unused_element,unnecessary_string_interpolations
class _CancelPlanService implements CancelPlanService {
_CancelPlanService(this._dio, {this.baseUrl, this.errorLogger});
final Dio _dio;
String? baseUrl;
final ParseErrorLogger? errorLogger;
@override
Future<void> cancelChosenPlan({
required int id,
CancelationModel? body,
}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
queryParameters.removeWhere((k, v) => v == null);
final _headers = <String, dynamic>{};
final _data = body;
final _options = _setStreamType<void>(
Options(method: 'POST', headers: _headers, extra: _extra)
.compose(
_dio.options,
'/api/chosen_plans/${id}/cancel',
queryParameters: queryParameters,
data: _data,
)
.copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
);
await _dio.fetch<void>(_options);
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||
requestOptions.responseType == ResponseType.stream)) {
if (T == String) {
requestOptions.responseType = ResponseType.plain;
} else {
requestOptions.responseType = ResponseType.json;
}
}
return requestOptions;
}
String _combineBaseUrls(String dioBaseUrl, String? baseUrl) {
if (baseUrl == null || baseUrl.trim().isEmpty) {
return dioBaseUrl;
}
final url = Uri.parse(baseUrl);
if (url.isAbsolute) {
return url.toString();
}
return Uri.parse(dioBaseUrl).resolveUri(url).toString();
}
}

View File

@@ -12,7 +12,8 @@ class ChatRoomModel with _$ChatRoomModel {
required String id,
@JsonKey(name: 'chat_room_setting') required ChatRoomSettingsModel chatRoomSettings,
required Users users,
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan) required ChosenPlanModel? chosenPlan,
@JsonKey(name: 'chosen_plan_id') required int? chosenPlanId,
ChosenPlanModel? chosenPlan,
}) = _ChatRoomModel;
factory ChatRoomModel.fromJson(Map<String, Object?> json) => _$ChatRoomModelFromJson(json);

View File

@@ -25,7 +25,8 @@ mixin _$ChatRoomModel {
ChatRoomSettingsModel get chatRoomSettings =>
throw _privateConstructorUsedError;
Users get users => throw _privateConstructorUsedError;
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
@JsonKey(name: 'chosen_plan_id')
int? get chosenPlanId => throw _privateConstructorUsedError;
ChosenPlanModel? get chosenPlan => throw _privateConstructorUsedError;
/// Serializes this ChatRoomModel to a JSON map.
@@ -49,7 +50,7 @@ abstract class $ChatRoomModelCopyWith<$Res> {
@JsonKey(name: 'chat_room_setting')
ChatRoomSettingsModel chatRoomSettings,
Users users,
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
@JsonKey(name: 'chosen_plan_id') int? chosenPlanId,
ChosenPlanModel? chosenPlan});
$ChatRoomSettingsModelCopyWith<$Res> get chatRoomSettings;
@@ -75,6 +76,7 @@ class _$ChatRoomModelCopyWithImpl<$Res, $Val extends ChatRoomModel>
Object? id = null,
Object? chatRoomSettings = null,
Object? users = null,
Object? chosenPlanId = freezed,
Object? chosenPlan = freezed,
}) {
return _then(_value.copyWith(
@@ -90,6 +92,10 @@ class _$ChatRoomModelCopyWithImpl<$Res, $Val extends ChatRoomModel>
? _value.users
: users // ignore: cast_nullable_to_non_nullable
as Users,
chosenPlanId: freezed == chosenPlanId
? _value.chosenPlanId
: chosenPlanId // ignore: cast_nullable_to_non_nullable
as int?,
chosenPlan: freezed == chosenPlan
? _value.chosenPlan
: chosenPlan // ignore: cast_nullable_to_non_nullable
@@ -146,7 +152,7 @@ abstract class _$$ChatRoomModelImplCopyWith<$Res>
@JsonKey(name: 'chat_room_setting')
ChatRoomSettingsModel chatRoomSettings,
Users users,
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
@JsonKey(name: 'chosen_plan_id') int? chosenPlanId,
ChosenPlanModel? chosenPlan});
@override
@@ -173,6 +179,7 @@ class __$$ChatRoomModelImplCopyWithImpl<$Res>
Object? id = null,
Object? chatRoomSettings = null,
Object? users = null,
Object? chosenPlanId = freezed,
Object? chosenPlan = freezed,
}) {
return _then(_$ChatRoomModelImpl(
@@ -188,6 +195,10 @@ class __$$ChatRoomModelImplCopyWithImpl<$Res>
? _value.users
: users // ignore: cast_nullable_to_non_nullable
as Users,
chosenPlanId: freezed == chosenPlanId
? _value.chosenPlanId
: chosenPlanId // ignore: cast_nullable_to_non_nullable
as int?,
chosenPlan: freezed == chosenPlan
? _value.chosenPlan
: chosenPlan // ignore: cast_nullable_to_non_nullable
@@ -203,8 +214,8 @@ class _$ChatRoomModelImpl implements _ChatRoomModel {
{required this.id,
@JsonKey(name: 'chat_room_setting') required this.chatRoomSettings,
required this.users,
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
required this.chosenPlan});
@JsonKey(name: 'chosen_plan_id') required this.chosenPlanId,
this.chosenPlan});
factory _$ChatRoomModelImpl.fromJson(Map<String, dynamic> json) =>
_$$ChatRoomModelImplFromJson(json);
@@ -217,12 +228,14 @@ class _$ChatRoomModelImpl implements _ChatRoomModel {
@override
final Users users;
@override
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
@JsonKey(name: 'chosen_plan_id')
final int? chosenPlanId;
@override
final ChosenPlanModel? chosenPlan;
@override
String toString() {
return 'ChatRoomModel(id: $id, chatRoomSettings: $chatRoomSettings, users: $users, chosenPlan: $chosenPlan)';
return 'ChatRoomModel(id: $id, chatRoomSettings: $chatRoomSettings, users: $users, chosenPlanId: $chosenPlanId, chosenPlan: $chosenPlan)';
}
@override
@@ -234,14 +247,16 @@ class _$ChatRoomModelImpl implements _ChatRoomModel {
(identical(other.chatRoomSettings, chatRoomSettings) ||
other.chatRoomSettings == chatRoomSettings) &&
(identical(other.users, users) || other.users == users) &&
(identical(other.chosenPlanId, chosenPlanId) ||
other.chosenPlanId == chosenPlanId) &&
(identical(other.chosenPlan, chosenPlan) ||
other.chosenPlan == chosenPlan));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(runtimeType, id, chatRoomSettings, users, chosenPlan);
int get hashCode => Object.hash(
runtimeType, id, chatRoomSettings, users, chosenPlanId, chosenPlan);
/// Create a copy of ChatRoomModel
/// with the given fields replaced by the non-null parameter values.
@@ -265,8 +280,8 @@ abstract class _ChatRoomModel implements ChatRoomModel {
@JsonKey(name: 'chat_room_setting')
required final ChatRoomSettingsModel chatRoomSettings,
required final Users users,
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
required final ChosenPlanModel? chosenPlan}) = _$ChatRoomModelImpl;
@JsonKey(name: 'chosen_plan_id') required final int? chosenPlanId,
final ChosenPlanModel? chosenPlan}) = _$ChatRoomModelImpl;
factory _ChatRoomModel.fromJson(Map<String, dynamic> json) =
_$ChatRoomModelImpl.fromJson;
@@ -279,7 +294,9 @@ abstract class _ChatRoomModel implements ChatRoomModel {
@override
Users get users;
@override
@JsonKey(name: 'chosen_plan', fromJson: parseChosenPlan)
@JsonKey(name: 'chosen_plan_id')
int? get chosenPlanId;
@override
ChosenPlanModel? get chosenPlan;
/// Create a copy of ChatRoomModel

View File

@@ -12,7 +12,11 @@ _$ChatRoomModelImpl _$$ChatRoomModelImplFromJson(Map<String, dynamic> json) =>
chatRoomSettings: ChatRoomSettingsModel.fromJson(
json['chat_room_setting'] as Map<String, dynamic>),
users: Users.fromJson(json['users'] as Map<String, dynamic>),
chosenPlan: parseChosenPlan(json['chosen_plan']),
chosenPlanId: (json['chosen_plan_id'] as num?)?.toInt(),
chosenPlan: json['chosenPlan'] == null
? null
: ChosenPlanModel.fromJson(
json['chosenPlan'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$ChatRoomModelImplToJson(_$ChatRoomModelImpl instance) =>
@@ -20,7 +24,8 @@ Map<String, dynamic> _$$ChatRoomModelImplToJson(_$ChatRoomModelImpl instance) =>
'id': instance.id,
'chat_room_setting': instance.chatRoomSettings,
'users': instance.users,
'chosen_plan': instance.chosenPlan,
'chosen_plan_id': instance.chosenPlanId,
'chosenPlan': instance.chosenPlan,
};
_$UsersImpl _$$UsersImplFromJson(Map<String, dynamic> json) => _$UsersImpl(

View File

@@ -14,7 +14,7 @@ import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_
import 'package:eatvio_app/features/chat/repositories/chat_repository.dart';
import 'package:eatvio_app/features/my_finance/models/card_model.dart';
import 'package:eatvio_app/features/my_finance/repositories/cards_repository.dart';
import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class ChatRoomCubit extends Cubit<ChatRoomState> {
@@ -57,13 +57,13 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
Future<void> checkCards() async {
try {
final cards = await cardsRepository.getCards();
setCard(cards: cards);
} catch (e) {
emit(const ChatRoomState.error(errorMessage: 'Ошибка получеия данных'));
_setCard(cards: cards);
} catch (e, st) {
log(e.toString(), stackTrace: st);
}
}
void setCard({required List<CardModel> cards}) {
void _setCard({required List<CardModel> cards}) {
final currentState = state;
if (currentState is ChatRoomStateLoaded) {
if (cards.where((card) => card.isDefault).isNotEmpty) {
@@ -75,15 +75,27 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
initChat(String chatId) async {
try {
final chatRoom = await chatRepository.getChatRoom(chatId);
ChosenPlanModel? chosenPlan;
if (chatRoom.chosenPlanId != null) {
chosenPlan = await chatRepository.getChosenPlan(id: chatRoom.chosenPlanId!);
}
final chatMessages = await chatRepository.getChatMessages(chatId, currentPage);
lastPage = chatMessages.meta.lastPage;
await _subscribeToChatChannel(chatId: chatId);
emit(
ChatRoomState.loaded(
chatRoom: chatRoom.copyWith(chosenPlan: chosenPlan),
chatMessages: chatMessages.messagesList,
isTyping: false,
isLoading: isLoading,
hasCard: false,
),
);
emit(ChatRoomState.loaded(chatRoom: chatRoom, chatMessages: chatMessages.messagesList, isTyping: false, isLoading: isLoading, hasCard: false));
// final cards = await cardsRepository.getCards();
// setCard(cards: cards);
if (chatRoom.chosenPlan?.consultantId == chatRoom.users.receiver.id) {
await checkCards();
}
} catch (e) {
log(e.toString());
typingSubscription?.cancel();
@@ -93,14 +105,26 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
}
}
Future<void> updateChosenPlanState() async {
final currentState = state;
if (currentState is ChatRoomStateLoaded) {
final chosenPlanId = currentState.chatRoom.chosenPlanId;
if (chosenPlanId != null) {
final chosenPlan = await chatRepository.getChosenPlan(id: chosenPlanId);
emit(currentState.copyWith.chatRoom(chosenPlan: chosenPlan));
}
}
}
Future<bool> choseStartPlanDate(DateTime date) async {
try {
if (state is! ChatRoomStateLoaded) return false;
final planId = (state as ChatRoomStateLoaded).chatRoom.chosenPlan?.id;
final planId = (state as ChatRoomStateLoaded).chatRoom.chosenPlanId;
if (planId == null) return false;
final stringDate = date.format('yyyy-MM-dd');
await chatRepository.choseStartPlanDate(id: planId, date: {'start_date': stringDate});
emit((state as ChatRoomStateLoaded).copyWith.chatRoom.chosenPlan?.call(chosenPlanState: ChosenPlanStateEnum.stateStartDateHasBeenSet) ?? state);
final chosenPlan = await chatRepository.getChosenPlan(id: planId);
emit((state as ChatRoomStateLoaded).copyWith.chatRoom(chosenPlan: chosenPlan));
return true;
} catch (e, st) {
log(e.toString(), stackTrace: st);
@@ -111,8 +135,7 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
Future<void> cancelChosenPlan({required String chatId, required int planId}) async {
try {
await chatRepository.cancelChosenPlan(id: planId);
final chatRoom = await chatRepository.getChatRoom(chatId);
emit((state as ChatRoomStateLoaded).copyWith(chatRoom: chatRoom));
emit((state as ChatRoomStateLoaded).copyWith.chatRoom(chosenPlan: null));
} catch (e) {
emit(const ChatRoomState.error(errorMessage: 'Ошибка получеия данных'));
}
@@ -133,7 +156,8 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
typingSubscription = wsManager.clientTypingStream?.listen((event) {
emit((state as ChatRoomStateLoaded).copyWith(isTyping: true, isLoading: isLoading));
EasyDebounce.debounce('client-typing', const Duration(seconds: 3), () => emit((state as ChatRoomStateLoaded).copyWith(isTyping: false, isLoading: isLoading)));
EasyDebounce.debounce(
'client-typing', const Duration(seconds: 3), () => emit((state as ChatRoomStateLoaded).copyWith(isTyping: false, isLoading: isLoading)));
});
}

View File

@@ -14,7 +14,6 @@ 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/features/chat/presentation/widgets/notification_banner.dart';
import 'package:eatvio_app/features/chat/presentation/widgets/order_card.dart';
import 'package:eatvio_app/features/feed/models/diets_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';
@@ -85,15 +84,24 @@ class _ChatScreenState extends State<ChatScreen> {
_buildChat(BuildContext context, ChatRoomStateLoaded state) {
final chosenPlan = state.chatRoom.chosenPlan;
final amIConsultant = chosenPlan?.consultantId == state.chatRoom.users.sender.id;
final showAboutOrderButton = showableChatBadgeStates.contains(chosenPlan?.chosenPlanState);
final showUserDetailsButton = amIConsultant &&
[
ChosenPlanStateEnum.stateStartDateHasBeenSet,
ChosenPlanStateEnum.statePendingPayment,
ChosenPlanStateEnum.stateInCoordination,
].contains(chosenPlan?.chosenPlanState);
final showAboutOrderButton =
state.chatRoom.chosenPlan != null && state.chatRoom.chosenPlan!.chosenPlanState != ChosenPlanStateEnum.stateCanceledByTheInitiator;
BannerType? bannerType;
if (amIConsultant && state.hasCard) {
if (chosenPlan == null) {
bannerType = null;
} else if (amIConsultant && state.hasCard) {
bannerType = BannerType.card;
} else if (amIConsultant && chosenPlan?.chosenPlanState == ChosenPlanStateEnum.stateInCoordination) {
} else if (amIConsultant && chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateInCoordination) {
bannerType = BannerType.date;
} else if (!amIConsultant &&
(chosenPlan?.chosenPlanState == ChosenPlanStateEnum.stateStartDateHasBeenSet ||
chosenPlan?.chosenPlanState == ChosenPlanStateEnum.statePendingPayment)) {
(chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateStartDateHasBeenSet || chosenPlan.chosenPlanState == ChosenPlanStateEnum.statePendingPayment)) {
bannerType = BannerType.payment;
}
return Scaffold(
@@ -254,15 +262,15 @@ class _ChatScreenState extends State<ChatScreen> {
itemCount: state.chatMessages.length + (state.isLoading ? 1 : 0),
),
const SizedBox(height: 16),
if (chosenPlan != null) ...[
OrderCard(
plan: chosenPlan.plan,
isCustomerView: !amIConsultant,
optionType: chosenPlan.optionType,
onCancel: () async {
await context.read<ChatRoomCubit>().cancelChosenPlan(chatId: state.chatRoom.id, planId: chosenPlan.id);
},
),
if (showUserDetailsButton) ...[
// OrderCard(
// plan: chosenPlan.plan,
// isCustomerView: !amIConsultant,
// optionType: chosenPlan.optionType,
// onCancel: () async {
// await context.read<ChatRoomCubit>().cancelChosenPlan(chatId: state.chatRoom.id, planId: chosenPlan.id);
// },
// ),
if (amIConsultant)
Padding(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
@@ -394,7 +402,7 @@ class _ChatScreenState extends State<ChatScreen> {
right: 15,
left: 16,
child: AboutOrderButton(
onTap: () {
onTap: () async {
GoRouter.of(context).push(InfoByOrderScreen.path, extra: {'planId': state.chatRoom.chosenPlan?.id});
},
)),

View File

@@ -5,6 +5,7 @@ import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_
import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.dart';
import 'package:eatvio_app/features/chat/presentation/pages/payment_screen.dart';
import 'package:eatvio_app/features/chat/presentation/widgets/chose_plan_date_widget.dart';
import 'package:eatvio_app/features/my_finance/presentation/pages/card_settings.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -76,7 +77,11 @@ class _NotificationBannerState extends State<NotificationBanner> {
),
const SizedBox(height: 12),
GestureDetector(
onTap: () {},
onTap: () {
GoRouter.of(context).pushReplacement(
CardSettingsScreen.path,
);
},
child: Text(s.add_card, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)),
),
const SizedBox(height: 16),

View File

@@ -7,6 +7,7 @@ import 'package:eatvio_app/features/chat/models/chat_room_model.dart';
import 'package:eatvio_app/features/chat/models/chat_room_pagination_model.dart';
import 'package:eatvio_app/features/chat/models/payment_model.dart';
import 'package:eatvio_app/features/chat/services/chat_service.dart';
import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
import 'package:http_parser/http_parser.dart';
class ChatRepository {
@@ -121,4 +122,14 @@ class ChatRepository {
rethrow;
}
}
Future<ChosenPlanModel> getChosenPlan({
required int id,
}) async {
try {
return await chatService.getChosenPlan(id: id);
} catch (e) {
rethrow;
}
}
}

View File

@@ -3,6 +3,7 @@ import 'package:eatvio_app/features/chat/models/chat_messages_model.dart';
import 'package:eatvio_app/features/chat/models/chat_room_model.dart';
import 'package:eatvio_app/features/chat/models/chat_room_pagination_model.dart';
import 'package:eatvio_app/features/chat/models/payment_model.dart';
import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
import 'package:retrofit/retrofit.dart';
part 'chat_service.g.dart';
@@ -62,4 +63,9 @@ abstract class ChatService {
Future<PaymentModel> getPaymentLink({
@Path() required int id,
});
@GET('/api/chosen_plans/show/{id}')
Future<ChosenPlanModel> getChosenPlan({
@Path() required int id,
});
}

View File

@@ -262,6 +262,33 @@ class _ChatService implements ChatService {
return _value;
}
@override
Future<ChosenPlanModel> getChosenPlan({required int id}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options = _setStreamType<ChosenPlanModel>(
Options(method: 'GET', headers: _headers, extra: _extra)
.compose(
_dio.options,
'/api/chosen_plans/show/${id}',
queryParameters: queryParameters,
data: _data,
)
.copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
);
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late ChosenPlanModel _value;
try {
_value = ChosenPlanModel.fromJson(_result.data!);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
return _value;
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||

View File

@@ -0,0 +1,64 @@
import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart';
import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'consultation_model.freezed.dart';
part 'consultation_model.g.dart';
@freezed
class ConsultationModel with _$ConsultationModel {
factory ConsultationModel({
required int id,
@JsonKey(name: 'last_state') required ChosenPlanStateEnum chosenPlanState,
required String planTitle,
required String userName,
required String userSlug,
required double consultantPayment,
required int duration,
required int currentDiaryDay,
@JsonKey(name: 'start_date', fromJson: parseDate) required DateTime startDate,
@JsonKey(name: 'end_date', fromJson: parseDate) required DateTime endDate,
@JsonKey(name: 'consultant_id') required int consultantId,
required List<DiaryModel> diaries,
}) = _ConsultationModel;
factory ConsultationModel.fromJson(Map<String, dynamic> json) => _$ConsultationModelFromJson(json);
}
@freezed
class DiaryModel with _$DiaryModel {
factory DiaryModel({
required int id,
required DiaryStateEnum state,
@JsonKey(fromJson: parseUnformattedDate) required DateTime date,
}) = _DiaryModel;
factory DiaryModel.fromJson(Map<String, dynamic> json) => _$DiaryModelFromJson(json);
}
DateTime parseUnformattedDate(String date) {
final dateChunks = date.split('/').map(int.parse).toList();
return DateTime(dateChunks.last, dateChunks.first, dateChunks[1]);
}
enum DiaryStateEnum {
init,
started,
finished,
commented,
@JsonValue('reviewed_by_user')
reviewedByUser,
@JsonValue('last_day_started')
lastDayStarted,
@JsonValue('last_day_finished')
lastDayFinished,
@JsonValue('last_day_commented')
lastDayCommented,
}
@freezed
class ConsultationPaginationModel with _$ConsultationPaginationModel {
factory ConsultationPaginationModel({required List<ConsultationModel> data}) = _ConsultationPaginationModel;
factory ConsultationPaginationModel.fromJson(Map<String, dynamic> json) => _$ConsultationPaginationModelFromJson(json);
}

View File

@@ -0,0 +1,799 @@
// 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 'consultation_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');
ConsultationModel _$ConsultationModelFromJson(Map<String, dynamic> json) {
return _ConsultationModel.fromJson(json);
}
/// @nodoc
mixin _$ConsultationModel {
int get id => throw _privateConstructorUsedError;
@JsonKey(name: 'last_state')
ChosenPlanStateEnum get chosenPlanState => throw _privateConstructorUsedError;
String get planTitle => throw _privateConstructorUsedError;
String get userName => throw _privateConstructorUsedError;
String get userSlug => throw _privateConstructorUsedError;
double get consultantPayment => throw _privateConstructorUsedError;
int get duration => throw _privateConstructorUsedError;
int get currentDiaryDay => throw _privateConstructorUsedError;
@JsonKey(name: 'start_date', fromJson: parseDate)
DateTime get startDate => throw _privateConstructorUsedError;
@JsonKey(name: 'end_date', fromJson: parseDate)
DateTime get endDate => throw _privateConstructorUsedError;
@JsonKey(name: 'consultant_id')
int get consultantId => throw _privateConstructorUsedError;
List<DiaryModel> get diaries => throw _privateConstructorUsedError;
/// Serializes this ConsultationModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of ConsultationModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ConsultationModelCopyWith<ConsultationModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ConsultationModelCopyWith<$Res> {
factory $ConsultationModelCopyWith(
ConsultationModel value, $Res Function(ConsultationModel) then) =
_$ConsultationModelCopyWithImpl<$Res, ConsultationModel>;
@useResult
$Res call(
{int id,
@JsonKey(name: 'last_state') ChosenPlanStateEnum chosenPlanState,
String planTitle,
String userName,
String userSlug,
double consultantPayment,
int duration,
int currentDiaryDay,
@JsonKey(name: 'start_date', fromJson: parseDate) DateTime startDate,
@JsonKey(name: 'end_date', fromJson: parseDate) DateTime endDate,
@JsonKey(name: 'consultant_id') int consultantId,
List<DiaryModel> diaries});
}
/// @nodoc
class _$ConsultationModelCopyWithImpl<$Res, $Val extends ConsultationModel>
implements $ConsultationModelCopyWith<$Res> {
_$ConsultationModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of ConsultationModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? chosenPlanState = null,
Object? planTitle = null,
Object? userName = null,
Object? userSlug = null,
Object? consultantPayment = null,
Object? duration = null,
Object? currentDiaryDay = null,
Object? startDate = null,
Object? endDate = null,
Object? consultantId = null,
Object? diaries = null,
}) {
return _then(_value.copyWith(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
chosenPlanState: null == chosenPlanState
? _value.chosenPlanState
: chosenPlanState // ignore: cast_nullable_to_non_nullable
as ChosenPlanStateEnum,
planTitle: null == planTitle
? _value.planTitle
: planTitle // ignore: cast_nullable_to_non_nullable
as String,
userName: null == userName
? _value.userName
: userName // ignore: cast_nullable_to_non_nullable
as String,
userSlug: null == userSlug
? _value.userSlug
: userSlug // ignore: cast_nullable_to_non_nullable
as String,
consultantPayment: null == consultantPayment
? _value.consultantPayment
: consultantPayment // ignore: cast_nullable_to_non_nullable
as double,
duration: null == duration
? _value.duration
: duration // ignore: cast_nullable_to_non_nullable
as int,
currentDiaryDay: null == currentDiaryDay
? _value.currentDiaryDay
: currentDiaryDay // ignore: cast_nullable_to_non_nullable
as int,
startDate: null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
endDate: null == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime,
consultantId: null == consultantId
? _value.consultantId
: consultantId // ignore: cast_nullable_to_non_nullable
as int,
diaries: null == diaries
? _value.diaries
: diaries // ignore: cast_nullable_to_non_nullable
as List<DiaryModel>,
) as $Val);
}
}
/// @nodoc
abstract class _$$ConsultationModelImplCopyWith<$Res>
implements $ConsultationModelCopyWith<$Res> {
factory _$$ConsultationModelImplCopyWith(_$ConsultationModelImpl value,
$Res Function(_$ConsultationModelImpl) then) =
__$$ConsultationModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{int id,
@JsonKey(name: 'last_state') ChosenPlanStateEnum chosenPlanState,
String planTitle,
String userName,
String userSlug,
double consultantPayment,
int duration,
int currentDiaryDay,
@JsonKey(name: 'start_date', fromJson: parseDate) DateTime startDate,
@JsonKey(name: 'end_date', fromJson: parseDate) DateTime endDate,
@JsonKey(name: 'consultant_id') int consultantId,
List<DiaryModel> diaries});
}
/// @nodoc
class __$$ConsultationModelImplCopyWithImpl<$Res>
extends _$ConsultationModelCopyWithImpl<$Res, _$ConsultationModelImpl>
implements _$$ConsultationModelImplCopyWith<$Res> {
__$$ConsultationModelImplCopyWithImpl(_$ConsultationModelImpl _value,
$Res Function(_$ConsultationModelImpl) _then)
: super(_value, _then);
/// Create a copy of ConsultationModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? chosenPlanState = null,
Object? planTitle = null,
Object? userName = null,
Object? userSlug = null,
Object? consultantPayment = null,
Object? duration = null,
Object? currentDiaryDay = null,
Object? startDate = null,
Object? endDate = null,
Object? consultantId = null,
Object? diaries = null,
}) {
return _then(_$ConsultationModelImpl(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
chosenPlanState: null == chosenPlanState
? _value.chosenPlanState
: chosenPlanState // ignore: cast_nullable_to_non_nullable
as ChosenPlanStateEnum,
planTitle: null == planTitle
? _value.planTitle
: planTitle // ignore: cast_nullable_to_non_nullable
as String,
userName: null == userName
? _value.userName
: userName // ignore: cast_nullable_to_non_nullable
as String,
userSlug: null == userSlug
? _value.userSlug
: userSlug // ignore: cast_nullable_to_non_nullable
as String,
consultantPayment: null == consultantPayment
? _value.consultantPayment
: consultantPayment // ignore: cast_nullable_to_non_nullable
as double,
duration: null == duration
? _value.duration
: duration // ignore: cast_nullable_to_non_nullable
as int,
currentDiaryDay: null == currentDiaryDay
? _value.currentDiaryDay
: currentDiaryDay // ignore: cast_nullable_to_non_nullable
as int,
startDate: null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
endDate: null == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime,
consultantId: null == consultantId
? _value.consultantId
: consultantId // ignore: cast_nullable_to_non_nullable
as int,
diaries: null == diaries
? _value._diaries
: diaries // ignore: cast_nullable_to_non_nullable
as List<DiaryModel>,
));
}
}
/// @nodoc
@JsonSerializable()
class _$ConsultationModelImpl implements _ConsultationModel {
_$ConsultationModelImpl(
{required this.id,
@JsonKey(name: 'last_state') required this.chosenPlanState,
required this.planTitle,
required this.userName,
required this.userSlug,
required this.consultantPayment,
required this.duration,
required this.currentDiaryDay,
@JsonKey(name: 'start_date', fromJson: parseDate) required this.startDate,
@JsonKey(name: 'end_date', fromJson: parseDate) required this.endDate,
@JsonKey(name: 'consultant_id') required this.consultantId,
required final List<DiaryModel> diaries})
: _diaries = diaries;
factory _$ConsultationModelImpl.fromJson(Map<String, dynamic> json) =>
_$$ConsultationModelImplFromJson(json);
@override
final int id;
@override
@JsonKey(name: 'last_state')
final ChosenPlanStateEnum chosenPlanState;
@override
final String planTitle;
@override
final String userName;
@override
final String userSlug;
@override
final double consultantPayment;
@override
final int duration;
@override
final int currentDiaryDay;
@override
@JsonKey(name: 'start_date', fromJson: parseDate)
final DateTime startDate;
@override
@JsonKey(name: 'end_date', fromJson: parseDate)
final DateTime endDate;
@override
@JsonKey(name: 'consultant_id')
final int consultantId;
final List<DiaryModel> _diaries;
@override
List<DiaryModel> get diaries {
if (_diaries is EqualUnmodifiableListView) return _diaries;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_diaries);
}
@override
String toString() {
return 'ConsultationModel(id: $id, chosenPlanState: $chosenPlanState, planTitle: $planTitle, userName: $userName, userSlug: $userSlug, consultantPayment: $consultantPayment, duration: $duration, currentDiaryDay: $currentDiaryDay, startDate: $startDate, endDate: $endDate, consultantId: $consultantId, diaries: $diaries)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ConsultationModelImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.chosenPlanState, chosenPlanState) ||
other.chosenPlanState == chosenPlanState) &&
(identical(other.planTitle, planTitle) ||
other.planTitle == planTitle) &&
(identical(other.userName, userName) ||
other.userName == userName) &&
(identical(other.userSlug, userSlug) ||
other.userSlug == userSlug) &&
(identical(other.consultantPayment, consultantPayment) ||
other.consultantPayment == consultantPayment) &&
(identical(other.duration, duration) ||
other.duration == duration) &&
(identical(other.currentDiaryDay, currentDiaryDay) ||
other.currentDiaryDay == currentDiaryDay) &&
(identical(other.startDate, startDate) ||
other.startDate == startDate) &&
(identical(other.endDate, endDate) || other.endDate == endDate) &&
(identical(other.consultantId, consultantId) ||
other.consultantId == consultantId) &&
const DeepCollectionEquality().equals(other._diaries, _diaries));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType,
id,
chosenPlanState,
planTitle,
userName,
userSlug,
consultantPayment,
duration,
currentDiaryDay,
startDate,
endDate,
consultantId,
const DeepCollectionEquality().hash(_diaries));
/// Create a copy of ConsultationModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ConsultationModelImplCopyWith<_$ConsultationModelImpl> get copyWith =>
__$$ConsultationModelImplCopyWithImpl<_$ConsultationModelImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$ConsultationModelImplToJson(
this,
);
}
}
abstract class _ConsultationModel implements ConsultationModel {
factory _ConsultationModel(
{required final int id,
@JsonKey(name: 'last_state')
required final ChosenPlanStateEnum chosenPlanState,
required final String planTitle,
required final String userName,
required final String userSlug,
required final double consultantPayment,
required final int duration,
required final int currentDiaryDay,
@JsonKey(name: 'start_date', fromJson: parseDate)
required final DateTime startDate,
@JsonKey(name: 'end_date', fromJson: parseDate)
required final DateTime endDate,
@JsonKey(name: 'consultant_id') required final int consultantId,
required final List<DiaryModel> diaries}) = _$ConsultationModelImpl;
factory _ConsultationModel.fromJson(Map<String, dynamic> json) =
_$ConsultationModelImpl.fromJson;
@override
int get id;
@override
@JsonKey(name: 'last_state')
ChosenPlanStateEnum get chosenPlanState;
@override
String get planTitle;
@override
String get userName;
@override
String get userSlug;
@override
double get consultantPayment;
@override
int get duration;
@override
int get currentDiaryDay;
@override
@JsonKey(name: 'start_date', fromJson: parseDate)
DateTime get startDate;
@override
@JsonKey(name: 'end_date', fromJson: parseDate)
DateTime get endDate;
@override
@JsonKey(name: 'consultant_id')
int get consultantId;
@override
List<DiaryModel> get diaries;
/// Create a copy of ConsultationModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ConsultationModelImplCopyWith<_$ConsultationModelImpl> get copyWith =>
throw _privateConstructorUsedError;
}
DiaryModel _$DiaryModelFromJson(Map<String, dynamic> json) {
return _DiaryModel.fromJson(json);
}
/// @nodoc
mixin _$DiaryModel {
int get id => throw _privateConstructorUsedError;
DiaryStateEnum get state => throw _privateConstructorUsedError;
@JsonKey(fromJson: parseUnformattedDate)
DateTime get date => throw _privateConstructorUsedError;
/// Serializes this DiaryModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of DiaryModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$DiaryModelCopyWith<DiaryModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $DiaryModelCopyWith<$Res> {
factory $DiaryModelCopyWith(
DiaryModel value, $Res Function(DiaryModel) then) =
_$DiaryModelCopyWithImpl<$Res, DiaryModel>;
@useResult
$Res call(
{int id,
DiaryStateEnum state,
@JsonKey(fromJson: parseUnformattedDate) DateTime date});
}
/// @nodoc
class _$DiaryModelCopyWithImpl<$Res, $Val extends DiaryModel>
implements $DiaryModelCopyWith<$Res> {
_$DiaryModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of DiaryModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? state = null,
Object? date = null,
}) {
return _then(_value.copyWith(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
state: null == state
? _value.state
: state // ignore: cast_nullable_to_non_nullable
as DiaryStateEnum,
date: null == date
? _value.date
: date // ignore: cast_nullable_to_non_nullable
as DateTime,
) as $Val);
}
}
/// @nodoc
abstract class _$$DiaryModelImplCopyWith<$Res>
implements $DiaryModelCopyWith<$Res> {
factory _$$DiaryModelImplCopyWith(
_$DiaryModelImpl value, $Res Function(_$DiaryModelImpl) then) =
__$$DiaryModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{int id,
DiaryStateEnum state,
@JsonKey(fromJson: parseUnformattedDate) DateTime date});
}
/// @nodoc
class __$$DiaryModelImplCopyWithImpl<$Res>
extends _$DiaryModelCopyWithImpl<$Res, _$DiaryModelImpl>
implements _$$DiaryModelImplCopyWith<$Res> {
__$$DiaryModelImplCopyWithImpl(
_$DiaryModelImpl _value, $Res Function(_$DiaryModelImpl) _then)
: super(_value, _then);
/// Create a copy of DiaryModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? state = null,
Object? date = null,
}) {
return _then(_$DiaryModelImpl(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
state: null == state
? _value.state
: state // ignore: cast_nullable_to_non_nullable
as DiaryStateEnum,
date: null == date
? _value.date
: date // ignore: cast_nullable_to_non_nullable
as DateTime,
));
}
}
/// @nodoc
@JsonSerializable()
class _$DiaryModelImpl implements _DiaryModel {
_$DiaryModelImpl(
{required this.id,
required this.state,
@JsonKey(fromJson: parseUnformattedDate) required this.date});
factory _$DiaryModelImpl.fromJson(Map<String, dynamic> json) =>
_$$DiaryModelImplFromJson(json);
@override
final int id;
@override
final DiaryStateEnum state;
@override
@JsonKey(fromJson: parseUnformattedDate)
final DateTime date;
@override
String toString() {
return 'DiaryModel(id: $id, state: $state, date: $date)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$DiaryModelImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.state, state) || other.state == state) &&
(identical(other.date, date) || other.date == date));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, id, state, date);
/// Create a copy of DiaryModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$DiaryModelImplCopyWith<_$DiaryModelImpl> get copyWith =>
__$$DiaryModelImplCopyWithImpl<_$DiaryModelImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$DiaryModelImplToJson(
this,
);
}
}
abstract class _DiaryModel implements DiaryModel {
factory _DiaryModel(
{required final int id,
required final DiaryStateEnum state,
@JsonKey(fromJson: parseUnformattedDate)
required final DateTime date}) = _$DiaryModelImpl;
factory _DiaryModel.fromJson(Map<String, dynamic> json) =
_$DiaryModelImpl.fromJson;
@override
int get id;
@override
DiaryStateEnum get state;
@override
@JsonKey(fromJson: parseUnformattedDate)
DateTime get date;
/// Create a copy of DiaryModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$DiaryModelImplCopyWith<_$DiaryModelImpl> get copyWith =>
throw _privateConstructorUsedError;
}
ConsultationPaginationModel _$ConsultationPaginationModelFromJson(
Map<String, dynamic> json) {
return _ConsultationPaginationModel.fromJson(json);
}
/// @nodoc
mixin _$ConsultationPaginationModel {
List<ConsultationModel> get data => throw _privateConstructorUsedError;
/// Serializes this ConsultationPaginationModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of ConsultationPaginationModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ConsultationPaginationModelCopyWith<ConsultationPaginationModel>
get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ConsultationPaginationModelCopyWith<$Res> {
factory $ConsultationPaginationModelCopyWith(
ConsultationPaginationModel value,
$Res Function(ConsultationPaginationModel) then) =
_$ConsultationPaginationModelCopyWithImpl<$Res,
ConsultationPaginationModel>;
@useResult
$Res call({List<ConsultationModel> data});
}
/// @nodoc
class _$ConsultationPaginationModelCopyWithImpl<$Res,
$Val extends ConsultationPaginationModel>
implements $ConsultationPaginationModelCopyWith<$Res> {
_$ConsultationPaginationModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of ConsultationPaginationModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? data = null,
}) {
return _then(_value.copyWith(
data: null == data
? _value.data
: data // ignore: cast_nullable_to_non_nullable
as List<ConsultationModel>,
) as $Val);
}
}
/// @nodoc
abstract class _$$ConsultationPaginationModelImplCopyWith<$Res>
implements $ConsultationPaginationModelCopyWith<$Res> {
factory _$$ConsultationPaginationModelImplCopyWith(
_$ConsultationPaginationModelImpl value,
$Res Function(_$ConsultationPaginationModelImpl) then) =
__$$ConsultationPaginationModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({List<ConsultationModel> data});
}
/// @nodoc
class __$$ConsultationPaginationModelImplCopyWithImpl<$Res>
extends _$ConsultationPaginationModelCopyWithImpl<$Res,
_$ConsultationPaginationModelImpl>
implements _$$ConsultationPaginationModelImplCopyWith<$Res> {
__$$ConsultationPaginationModelImplCopyWithImpl(
_$ConsultationPaginationModelImpl _value,
$Res Function(_$ConsultationPaginationModelImpl) _then)
: super(_value, _then);
/// Create a copy of ConsultationPaginationModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? data = null,
}) {
return _then(_$ConsultationPaginationModelImpl(
data: null == data
? _value._data
: data // ignore: cast_nullable_to_non_nullable
as List<ConsultationModel>,
));
}
}
/// @nodoc
@JsonSerializable()
class _$ConsultationPaginationModelImpl
implements _ConsultationPaginationModel {
_$ConsultationPaginationModelImpl(
{required final List<ConsultationModel> data})
: _data = data;
factory _$ConsultationPaginationModelImpl.fromJson(
Map<String, dynamic> json) =>
_$$ConsultationPaginationModelImplFromJson(json);
final List<ConsultationModel> _data;
@override
List<ConsultationModel> get data {
if (_data is EqualUnmodifiableListView) return _data;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_data);
}
@override
String toString() {
return 'ConsultationPaginationModel(data: $data)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ConsultationPaginationModelImpl &&
const DeepCollectionEquality().equals(other._data, _data));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(runtimeType, const DeepCollectionEquality().hash(_data));
/// Create a copy of ConsultationPaginationModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ConsultationPaginationModelImplCopyWith<_$ConsultationPaginationModelImpl>
get copyWith => __$$ConsultationPaginationModelImplCopyWithImpl<
_$ConsultationPaginationModelImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$ConsultationPaginationModelImplToJson(
this,
);
}
}
abstract class _ConsultationPaginationModel
implements ConsultationPaginationModel {
factory _ConsultationPaginationModel(
{required final List<ConsultationModel> data}) =
_$ConsultationPaginationModelImpl;
factory _ConsultationPaginationModel.fromJson(Map<String, dynamic> json) =
_$ConsultationPaginationModelImpl.fromJson;
@override
List<ConsultationModel> get data;
/// Create a copy of ConsultationPaginationModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ConsultationPaginationModelImplCopyWith<_$ConsultationPaginationModelImpl>
get copyWith => throw _privateConstructorUsedError;
}

View File

@@ -0,0 +1,100 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'consultation_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$ConsultationModelImpl _$$ConsultationModelImplFromJson(
Map<String, dynamic> json) =>
_$ConsultationModelImpl(
id: (json['id'] as num).toInt(),
chosenPlanState:
$enumDecode(_$ChosenPlanStateEnumEnumMap, json['last_state']),
planTitle: json['planTitle'] as String,
userName: json['userName'] as String,
userSlug: json['userSlug'] as String,
consultantPayment: (json['consultantPayment'] as num).toDouble(),
duration: (json['duration'] as num).toInt(),
currentDiaryDay: (json['currentDiaryDay'] as num).toInt(),
startDate: parseDate(json['start_date'] as String),
endDate: parseDate(json['end_date'] as String),
consultantId: (json['consultant_id'] as num).toInt(),
diaries: (json['diaries'] as List<dynamic>)
.map((e) => DiaryModel.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> _$$ConsultationModelImplToJson(
_$ConsultationModelImpl instance) =>
<String, dynamic>{
'id': instance.id,
'last_state': _$ChosenPlanStateEnumEnumMap[instance.chosenPlanState]!,
'planTitle': instance.planTitle,
'userName': instance.userName,
'userSlug': instance.userSlug,
'consultantPayment': instance.consultantPayment,
'duration': instance.duration,
'currentDiaryDay': instance.currentDiaryDay,
'start_date': instance.startDate.toIso8601String(),
'end_date': instance.endDate.toIso8601String(),
'consultant_id': instance.consultantId,
'diaries': instance.diaries,
};
const _$ChosenPlanStateEnumEnumMap = {
ChosenPlanStateEnum.stateInited: 'initied',
ChosenPlanStateEnum.stateStartDateHasBeenSet: 'start_date_has_been_set',
ChosenPlanStateEnum.stateInCoordination: 'in_coordination',
ChosenPlanStateEnum.stateStarted: 'started',
ChosenPlanStateEnum.stateInProgress: 'in_progress',
ChosenPlanStateEnum.statePendingPayment: 'pending_payment',
ChosenPlanStateEnum.stateConfirmedByConsultant: 'confirmed_by_consultant',
ChosenPlanStateEnum.statePaid: 'paid',
ChosenPlanStateEnum.stateCanceledByTheInitiator: 'canceled_by_the_initiator',
ChosenPlanStateEnum.stateLastDayStarted: 'last_day_started',
ChosenPlanStateEnum.stateLastDayFinished: 'last_day_finished',
ChosenPlanStateEnum.stateComplained: 'complained',
ChosenPlanStateEnum.stateDone: 'done',
ChosenPlanStateEnum.stateChargeWasCollected: 'charge_was_collected',
};
_$DiaryModelImpl _$$DiaryModelImplFromJson(Map<String, dynamic> json) =>
_$DiaryModelImpl(
id: (json['id'] as num).toInt(),
state: $enumDecode(_$DiaryStateEnumEnumMap, json['state']),
date: parseUnformattedDate(json['date'] as String),
);
Map<String, dynamic> _$$DiaryModelImplToJson(_$DiaryModelImpl instance) =>
<String, dynamic>{
'id': instance.id,
'state': _$DiaryStateEnumEnumMap[instance.state]!,
'date': instance.date.toIso8601String(),
};
const _$DiaryStateEnumEnumMap = {
DiaryStateEnum.init: 'init',
DiaryStateEnum.started: 'started',
DiaryStateEnum.finished: 'finished',
DiaryStateEnum.commented: 'commented',
DiaryStateEnum.reviewedByUser: 'reviewed_by_user',
DiaryStateEnum.lastDayStarted: 'last_day_started',
DiaryStateEnum.lastDayFinished: 'last_day_finished',
DiaryStateEnum.lastDayCommented: 'last_day_commented',
};
_$ConsultationPaginationModelImpl _$$ConsultationPaginationModelImplFromJson(
Map<String, dynamic> json) =>
_$ConsultationPaginationModelImpl(
data: (json['data'] as List<dynamic>)
.map((e) => ConsultationModel.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> _$$ConsultationPaginationModelImplToJson(
_$ConsultationPaginationModelImpl instance) =>
<String, dynamic>{
'data': instance.data,
};

View File

@@ -0,0 +1,21 @@
import 'dart:developer';
import 'package:eatvio_app/core/enums/base_state.dart';
import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_state.dart';
import 'package:eatvio_app/features/consultations/repositories/consultations_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class ConsultationsCubit extends Cubit<ConsultationsState> {
final ConsultationsRepository consultationsRepository;
ConsultationsCubit({required this.consultationsRepository}) : super(ConsultationsState());
Future<void> loadConsultations() async {
try {
final consultations = await consultationsRepository.getConsultations();
emit(state.copyWith(consultations: consultations, state: BaseState.success));
} catch (e, st) {
log(e.toString(), stackTrace: st);
emit(state.copyWith(state: BaseState.error));
}
}
}

View File

@@ -0,0 +1,15 @@
import 'package:eatvio_app/core/enums/base_state.dart';
import 'package:eatvio_app/features/consultations/models/consultation_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'consultations_state.freezed.dart';
@freezed
class ConsultationsState with _$ConsultationsState {
factory ConsultationsState({
@Default(BaseState.initial) BaseState state,
@Default([]) List<ConsultationModel> consultations
}) = _ConsultationsState;
}

View File

@@ -0,0 +1,175 @@
// 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 'consultations_state.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');
/// @nodoc
mixin _$ConsultationsState {
BaseState get state => throw _privateConstructorUsedError;
List<ConsultationModel> get consultations =>
throw _privateConstructorUsedError;
/// Create a copy of ConsultationsState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ConsultationsStateCopyWith<ConsultationsState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ConsultationsStateCopyWith<$Res> {
factory $ConsultationsStateCopyWith(
ConsultationsState value, $Res Function(ConsultationsState) then) =
_$ConsultationsStateCopyWithImpl<$Res, ConsultationsState>;
@useResult
$Res call({BaseState state, List<ConsultationModel> consultations});
}
/// @nodoc
class _$ConsultationsStateCopyWithImpl<$Res, $Val extends ConsultationsState>
implements $ConsultationsStateCopyWith<$Res> {
_$ConsultationsStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of ConsultationsState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? state = null,
Object? consultations = null,
}) {
return _then(_value.copyWith(
state: null == state
? _value.state
: state // ignore: cast_nullable_to_non_nullable
as BaseState,
consultations: null == consultations
? _value.consultations
: consultations // ignore: cast_nullable_to_non_nullable
as List<ConsultationModel>,
) as $Val);
}
}
/// @nodoc
abstract class _$$ConsultationsStateImplCopyWith<$Res>
implements $ConsultationsStateCopyWith<$Res> {
factory _$$ConsultationsStateImplCopyWith(_$ConsultationsStateImpl value,
$Res Function(_$ConsultationsStateImpl) then) =
__$$ConsultationsStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({BaseState state, List<ConsultationModel> consultations});
}
/// @nodoc
class __$$ConsultationsStateImplCopyWithImpl<$Res>
extends _$ConsultationsStateCopyWithImpl<$Res, _$ConsultationsStateImpl>
implements _$$ConsultationsStateImplCopyWith<$Res> {
__$$ConsultationsStateImplCopyWithImpl(_$ConsultationsStateImpl _value,
$Res Function(_$ConsultationsStateImpl) _then)
: super(_value, _then);
/// Create a copy of ConsultationsState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? state = null,
Object? consultations = null,
}) {
return _then(_$ConsultationsStateImpl(
state: null == state
? _value.state
: state // ignore: cast_nullable_to_non_nullable
as BaseState,
consultations: null == consultations
? _value._consultations
: consultations // ignore: cast_nullable_to_non_nullable
as List<ConsultationModel>,
));
}
}
/// @nodoc
class _$ConsultationsStateImpl implements _ConsultationsState {
_$ConsultationsStateImpl(
{this.state = BaseState.initial,
final List<ConsultationModel> consultations = const []})
: _consultations = consultations;
@override
@JsonKey()
final BaseState state;
final List<ConsultationModel> _consultations;
@override
@JsonKey()
List<ConsultationModel> get consultations {
if (_consultations is EqualUnmodifiableListView) return _consultations;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_consultations);
}
@override
String toString() {
return 'ConsultationsState(state: $state, consultations: $consultations)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ConsultationsStateImpl &&
(identical(other.state, state) || other.state == state) &&
const DeepCollectionEquality()
.equals(other._consultations, _consultations));
}
@override
int get hashCode => Object.hash(
runtimeType, state, const DeepCollectionEquality().hash(_consultations));
/// Create a copy of ConsultationsState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ConsultationsStateImplCopyWith<_$ConsultationsStateImpl> get copyWith =>
__$$ConsultationsStateImplCopyWithImpl<_$ConsultationsStateImpl>(
this, _$identity);
}
abstract class _ConsultationsState implements ConsultationsState {
factory _ConsultationsState(
{final BaseState state,
final List<ConsultationModel> consultations}) = _$ConsultationsStateImpl;
@override
BaseState get state;
@override
List<ConsultationModel> get consultations;
/// Create a copy of ConsultationsState
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ConsultationsStateImplCopyWith<_$ConsultationsStateImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -1,33 +1,61 @@
import 'package:eatvio_app/features/consultations/entities/consultations_entity.dart';
import 'package:eatvio_app/core/enums/base_state.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/widgets/data_error_widget.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_cubit.dart';
import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_state.dart';
import 'package:eatvio_app/features/consultations/presentation/widgets/consultation_card_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class ConsultationsScreen extends StatelessWidget {
static const path = '/consultations-screen';
static const name = 'consultationsScreen';
const ConsultationsScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF0F2F5),
appBar: AppBar(
title: const Text('Консультации'),
centerTitle: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadiusDirectional.vertical(
bottom: Radius.circular(12),
),
),
),
body: ListView.separated(
padding: const EdgeInsets.symmetric(vertical: 12),
itemBuilder: (context, index) => ConsultationCardWidget(
consultationsEntity: testData[index],
),
separatorBuilder: (context, index) => const SizedBox(
height: 12,
),
itemCount: testData.length,
),
return BlocProvider(
create: (context) => sl<ConsultationsCubit>()..loadConsultations(),
child: const ConsultationsContent(),
);
}
}
class ConsultationsContent extends StatelessWidget {
const ConsultationsContent({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<ConsultationsCubit, ConsultationsState>(
builder: (context, state) {
return Scaffold(
backgroundColor: const Color(0xFFF0F2F5),
appBar: AppBar(
title: const Text('Консультации'),
centerTitle: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadiusDirectional.vertical(
bottom: Radius.circular(12),
),
),
),
body: switch (state.state) {
BaseState.loading => const Loader(),
BaseState.error => DataErrorWidget(onReload: () async {}),
BaseState.initial => const Loader(),
BaseState.success => ListView.separated(
padding: const EdgeInsets.symmetric(vertical: 12),
itemBuilder: (context, index) => ConsultationCardWidget(
consultationsModel: state.consultations[index],
),
separatorBuilder: (context, index) => const SizedBox(
height: 12,
),
itemCount: state.consultations.length,
),
});
},
);
}
}

View File

@@ -1,37 +1,66 @@
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/core/widgets/custom_divider.dart';
import 'package:eatvio_app/features/consultations/entities/consultations_entity.dart';
import 'package:eatvio_app/core/widgets/show_error_dialog.dart';
import 'package:eatvio_app/features/consultations/models/consultation_model.dart';
import 'package:eatvio_app/features/diary/presentation/pages/diary_screen.dart';
import 'package:eatvio_app/features/user_details/presentation/user_details_screen.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:table_calendar/table_calendar.dart';
class ConsultationCardWidget extends StatelessWidget {
final List<String> titles = const [
'Дневник не заполнялся и никаких движений в тот день не было.',
'Показывает границы плана питания или публичной диеты в будущем.',
'Начат дневник питания, но не закончен.',
'Дневник закончен и прокомментирован консультантом но не просмотрен пользователем.',
'Дневник заполнен, но не прокомментирован консультантом.',
'Дневник заполнен, прокомментирован консультантом и после этого клиент просмотрел комментарии.',
];
final List<Color> legendColors = const [
Colors.white,
Color(0xFFCAC9C9),
Color(0xFFF0C497),
Color(0xFF2F80ED),
Color(0xFFFF9500),
Color(0xFF4CD964),
];
final ConsultationsEntity consultationsEntity;
class ConsultationCardWidget extends StatefulWidget {
final ConsultationModel consultationsModel;
const ConsultationCardWidget({
super.key,
required this.consultationsEntity,
required this.consultationsModel,
});
@override
State<ConsultationCardWidget> createState() => _ConsultationCardWidgetState();
}
class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
final List<String> titles = const [
'Дневник не заполнялся и никаких движений в тот день не было.',
'Начат дневник питания, но не закончен.',
'Дневник заполнен, но не прокомментирован консультантом.',
'Дневник закончен и прокомментирован консультантом но не просмотрен пользователем.',
'Дневник заполнен, прокомментирован консультантом и после этого клиент просмотрел комментарии.',
];
final List<Color> legendColors = const [
Colors.white,
Color(0xFFF0C497),
Color(0xFFFF9500),
Color(0xFF2F80ED),
Color(0xFF4CD964),
];
late final List<(int, DiaryModel)> givenDaysWithDayNumber;
final List<List<DiaryStateEnum>> legendStates = [
[DiaryStateEnum.init],
[DiaryStateEnum.started, DiaryStateEnum.lastDayStarted],
[DiaryStateEnum.finished, DiaryStateEnum.lastDayFinished],
[DiaryStateEnum.commented, DiaryStateEnum.lastDayCommented],
[DiaryStateEnum.reviewedByUser]
];
@override
void initState() {
super.initState();
final startPlanDate = widget.consultationsModel.startDate;
givenDaysWithDayNumber = List.generate(widget.consultationsModel.diaries.length, (index) {
final dayNumber = widget.consultationsModel.diaries[index].date.difference(startPlanDate).inDays;
return (dayNumber, widget.consultationsModel.diaries[index]);
});
widget.consultationsModel.diaries;
}
@override
Widget build(BuildContext context) {
final s = S.of(context);
return Material(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
@@ -50,7 +79,7 @@ class ConsultationCardWidget extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
consultationsEntity.name,
widget.consultationsModel.userName,
style: const TextStyle(
height: 1,
color: Color(0xFF323F4B),
@@ -60,7 +89,7 @@ class ConsultationCardWidget extends StatelessWidget {
),
const SizedBox(height: 4),
Text(
consultationsEntity.planName,
widget.consultationsModel.planTitle,
style: const TextStyle(
height: 1,
color: Color(0xFF88949F),
@@ -75,7 +104,7 @@ class ConsultationCardWidget extends StatelessWidget {
const SizedBox(height: 12),
GestureDetector(
onTap: () {
GoRouter.of(context).pushNamed(UserDetailsScreen.name, extra: "id-754786b9-4ae6-4677-b847-1d05db44dfb9");
GoRouter.of(context).pushNamed(UserDetailsScreen.name, extra: widget.consultationsModel.userSlug);
},
child: Text(
'Анкета пользователя',
@@ -95,7 +124,7 @@ class ConsultationCardWidget extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'День ${consultationsEntity.currentDayOfPlan} из ${consultationsEntity.totalDaysOfPlan}',
'День ${widget.consultationsModel.currentDiaryDay} из ${widget.consultationsModel.duration}',
style: const TextStyle(
height: 1,
color: Color(0xFF323F4B),
@@ -148,45 +177,102 @@ class ConsultationCardWidget extends StatelessWidget {
],
),
const SizedBox(height: 16),
TableCalendar(
focusedDay: DateTime.now(),
firstDay: DateTime.utc(2020, 10, 16),
lastDay: DateTime.utc(2030, 3, 14),
locale: 'ru_RU',
startingDayOfWeek: StartingDayOfWeek.monday,
headerVisible: false,
calendarFormat: CalendarFormat.week,
calendarStyle: CalendarStyle(
weekendDecoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
defaultDecoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
selectedDecoration: BoxDecoration(
color: appTheme.blueA400,
shape: BoxShape.circle,
),
todayTextStyle: const TextStyle(
color: Colors.black,
),
todayDecoration: BoxDecoration(
border: Border.all(
color: appTheme.blueA400,
width: 2,
),
color: Colors.white,
shape: BoxShape.circle,
),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: List.generate(widget.consultationsModel.duration, (index) {
final day = givenDaysWithDayNumber.firstWhereOrNull((e) => e.$1 == index);
final hasActivity = day != null && day.$2.state != DiaryStateEnum.init;
return Padding(
padding: const EdgeInsets.only(right: 12),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
InkWell(
onTap: () {
if (!hasActivity) {
showErrorDialog(context,
title: s.you_can_not_open_this_day, description: '${widget.consultationsModel.userName} ${s.has_not_filled_it_yet}');
return;
}
GoRouter.of(context).push(UserDiaryScreen.path, extra: day.$2.id);
},
borderRadius: BorderRadius.circular(100),
child: Ink(
decoration: BoxDecoration(
color: hasActivity ? appTheme.dividerColor : appTheme.dividerColor.withValues(alpha: 0.4),
borderRadius: BorderRadius.circular(100)),
width: 58,
height: 32,
child: Center(
child: Text(
(index + 1).toString(),
style: TextStyles.textMSemibold.copyWith(
color: hasActivity ? appTheme.gray : appTheme.gray.withValues(alpha: 0.4),
),
),
),
),
),
if (hasActivity) ...[
const SizedBox(
height: 4,
),
switch (day.$2.state) {
DiaryStateEnum.init => const SizedBox.shrink(),
DiaryStateEnum.started => Container(
width: 8,
height: 8,
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[1]),
),
DiaryStateEnum.lastDayStarted => Container(
width: 8,
height: 8,
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[1]),
),
DiaryStateEnum.finished => Container(
width: 8,
height: 8,
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[2]),
),
DiaryStateEnum.lastDayFinished => Container(
width: 8,
height: 8,
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[2]),
),
DiaryStateEnum.commented => Container(
width: 8,
height: 8,
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[3]),
),
DiaryStateEnum.lastDayCommented => Container(
width: 8,
height: 8,
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[3]),
),
DiaryStateEnum.reviewedByUser => Container(
width: 8,
height: 8,
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[4]),
),
},
const SizedBox(
height: 12,
),
] else
const SizedBox(
height: 24,
),
],
),
);
}),
),
),
const SizedBox(height: 16),
const CustomDivider(),
const SizedBox(height: 12),
Text(
'${consultationsEntity.paymentAmount} ₽',
'${widget.consultationsModel.consultantPayment.toStringAsFixed(0)} ₽',
style: const TextStyle(
color: Color(0xFF019A3E),
fontSize: 20,
@@ -198,7 +284,19 @@ class ConsultationCardWidget extends StatelessWidget {
children: [
InkWell(
borderRadius: BorderRadius.circular(20),
onTap: () {},
onTap: () {
final lastActiveDay = givenDaysWithDayNumber
.where(
(e) => e.$2.state == DiaryStateEnum.finished || e.$2.state == DiaryStateEnum.lastDayFinished,
)
.lastOrNull;
if (lastActiveDay != null) {
GoRouter.of(context).push(UserDiaryScreen.path, extra: lastActiveDay.$2.id);
return;
}
showErrorDialog(context,
title: s.you_can_not_open_this_day, description: '${widget.consultationsModel.userName} ${s.has_not_filled_it_yet}');
},
child: Ink(
width: MediaQuery.of(context).size.width - 32 - 52 - 8,
padding: const EdgeInsets.all(16),

View File

@@ -0,0 +1,16 @@
import 'package:eatvio_app/features/consultations/models/consultation_model.dart';
import 'package:eatvio_app/features/consultations/services/consultation_service.dart';
class ConsultationsRepository {
final ConsultationService consultationService;
ConsultationsRepository({required this.consultationService});
Future<List<ConsultationModel>> getConsultations() async {
try {
final consultations = await consultationService.getConsultations();
return consultations.data;
} catch (e) {
rethrow;
}
}
}

View File

@@ -0,0 +1,14 @@
import 'package:dio/dio.dart';
import 'package:eatvio_app/features/consultations/models/consultation_model.dart';
import 'package:retrofit/retrofit.dart';
part 'consultation_service.g.dart';
@RestApi()
abstract class ConsultationService {
factory ConsultationService(Dio dio, {String baseUrl}) = _ConsultationService;
@GET('/api/consultations')
Future<ConsultationPaginationModel> getConsultations();
}

View File

@@ -0,0 +1,73 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'consultation_service.dart';
// **************************************************************************
// RetrofitGenerator
// **************************************************************************
// ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers,unused_element,unnecessary_string_interpolations
class _ConsultationService implements ConsultationService {
_ConsultationService(this._dio, {this.baseUrl, this.errorLogger});
final Dio _dio;
String? baseUrl;
final ParseErrorLogger? errorLogger;
@override
Future<ConsultationPaginationModel> getConsultations() async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options = _setStreamType<ConsultationPaginationModel>(
Options(method: 'GET', headers: _headers, extra: _extra)
.compose(
_dio.options,
'/api/consultations',
queryParameters: queryParameters,
data: _data,
)
.copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
);
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late ConsultationPaginationModel _value;
try {
_value = ConsultationPaginationModel.fromJson(_result.data!);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
return _value;
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||
requestOptions.responseType == ResponseType.stream)) {
if (T == String) {
requestOptions.responseType = ResponseType.plain;
} else {
requestOptions.responseType = ResponseType.json;
}
}
return requestOptions;
}
String _combineBaseUrls(String dioBaseUrl, String? baseUrl) {
if (baseUrl == null || baseUrl.trim().isEmpty) {
return dioBaseUrl;
}
final url = Uri.parse(baseUrl);
if (url.isAbsolute) {
return url.toString();
}
return Uri.parse(dioBaseUrl).resolveUri(url).toString();
}
}

View File

@@ -124,7 +124,7 @@ class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
const SizedBox(
height: 4,
),
CustomErrorWidget(error: state.dietNameError ?? ''),
ValidationErrorWidget(error: state.dietNameError ?? ''),
],
const SizedBox(height: 12),
SizedBox(
@@ -146,7 +146,7 @@ class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
const SizedBox(
height: 4,
),
CustomErrorWidget(error: state.dietDescriptionError ?? ''),
ValidationErrorWidget(error: state.dietDescriptionError ?? ''),
],
const SizedBox(height: 12),
const DietDurationDropDownWidget(),

View File

@@ -218,7 +218,7 @@ class _DayWidgetState extends State<DayWidget> {
),
if (state.dailyScheduleErrors[widget.index].isNotEmpty) ...[
const SizedBox(height: 12),
CustomErrorWidget(error: state.dailyScheduleErrors[widget.index]),
ValidationErrorWidget(error: state.dailyScheduleErrors[widget.index]),
],
const SizedBox(height: 20),
],

View File

@@ -68,7 +68,7 @@ class _DietDurationDropDownWidgetState extends State<DietDurationDropDownWidget>
const SizedBox(
height: 4,
),
CustomErrorWidget(error: state.dietDurationError ?? ''),
ValidationErrorWidget(error: state.dietDurationError ?? ''),
],
],
);

View File

@@ -155,7 +155,7 @@ class _DietImagePickerWidgetState extends State<DietImagePickerWidget> {
),
),
),
if (state.dietImageError != null) ...[const SizedBox(height: 8), CustomErrorWidget(error: state.dietImageError ?? '')],
if (state.dietImageError != null) ...[const SizedBox(height: 8), ValidationErrorWidget(error: state.dietImageError ?? '')],
],
);
},

View File

@@ -3,8 +3,8 @@ import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
class CustomErrorWidget extends StatelessWidget {
const CustomErrorWidget({super.key, required this.error});
class ValidationErrorWidget extends StatelessWidget {
const ValidationErrorWidget({super.key, required this.error});
final String error;
@override
Widget build(BuildContext context) {

View File

@@ -76,7 +76,7 @@ class _StatusDropDownWidgetState extends State<StatusDropDownWidget> {
const SizedBox(
height: 4,
),
CustomErrorWidget(error: state.dietStateError ?? ''),
ValidationErrorWidget(error: state.dietStateError ?? ''),
],
],
);

View File

@@ -60,7 +60,7 @@ class _InfoAboutRecipeWidgetState extends State<InfoAboutRecipeWidget> {
),
if (state.titleValidationError != null) ...[
const SizedBox(height: 4),
CustomErrorWidget(
ValidationErrorWidget(
error: state.titleValidationError!,
),
],
@@ -111,7 +111,7 @@ class _InfoAboutRecipeWidgetState extends State<InfoAboutRecipeWidget> {
),
if (state.descriptionValidationError != null) ...[
const SizedBox(height: 4),
CustomErrorWidget(
ValidationErrorWidget(
error: state.descriptionValidationError!,
),
],
@@ -135,7 +135,7 @@ class _InfoAboutRecipeWidgetState extends State<InfoAboutRecipeWidget> {
),
if (state.imageValidationError != null) ...[
const SizedBox(height: 4),
CustomErrorWidget(
ValidationErrorWidget(
error: state.imageValidationError!,
),
],

View File

@@ -55,7 +55,7 @@ class _PortionWidgetState extends State<PortionWidget> {
),
if (state.servingsValidationError != null) ...[
const SizedBox(height: 4),
CustomErrorWidget(
ValidationErrorWidget(
error: state.servingsValidationError!,
),
],
@@ -88,7 +88,7 @@ class _PortionWidgetState extends State<PortionWidget> {
),
if (state.energyPerServingValidationError != null) ...[
const SizedBox(height: 4),
CustomErrorWidget(
ValidationErrorWidget(
error: state.energyPerServingValidationError!,
),
],

View File

@@ -82,7 +82,7 @@ class _TimeOfCookWidgetState extends State<TimeOfCookWidget> {
),
if (state.cookingTimeValidationError != null) ...[
const SizedBox(height: 4),
CustomErrorWidget(
ValidationErrorWidget(
error: state.cookingTimeValidationError!,
),
],
@@ -108,7 +108,7 @@ class _TimeOfCookWidgetState extends State<TimeOfCookWidget> {
),
if (state.cookingTimeValidationError != null) ...[
const SizedBox(height: 4),
CustomErrorWidget(
ValidationErrorWidget(
error: state.cookingTimeValidationError!,
),
],
@@ -169,7 +169,7 @@ class _TimeOfCookWidgetState extends State<TimeOfCookWidget> {
),
if (state.activeTimeValidationError != null) ...[
const SizedBox(height: 4),
CustomErrorWidget(
ValidationErrorWidget(
error: state.activeTimeValidationError!,
),
],
@@ -196,7 +196,7 @@ class _TimeOfCookWidgetState extends State<TimeOfCookWidget> {
),
if (state.activeTimeValidationError != null) ...[
const SizedBox(height: 4),
CustomErrorWidget(
ValidationErrorWidget(
error: state.activeTimeValidationError!,
),
],

View File

@@ -0,0 +1,36 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'consultant_comment_model.freezed.dart';
part 'consultant_comment_model.g.dart';
@freezed
class ConsultantCommentModel with _$ConsultantCommentModel {
factory ConsultantCommentModel({
String? comment,
@Default({}) @JsonKey(name: 'meals') Map<String, MealCommentModel> mealsComments,
@Default([]) List<PortionCommentModel> portions,
}) = _ConsultantCommentModel;
factory ConsultantCommentModel.fromJson(Map<String, dynamic> json) => _$ConsultantCommentModelFromJson(json);
}
@freezed
class PortionCommentModel with _$PortionCommentModel {
factory PortionCommentModel({
required String comment,
required int id,
}) = _PortionCommentModel;
factory PortionCommentModel.fromJson(Map<String, dynamic> json) => _$PortionCommentModelFromJson(json);
}
@freezed
class MealCommentModel with _$MealCommentModel {
factory MealCommentModel({
required String comment,
@JsonKey(name: 'type_no')required int typeNumber
}) = _MealCommentModel;
factory MealCommentModel.fromJson(Map<String, dynamic> json) => _$MealCommentModelFromJson(json);
}

View File

@@ -0,0 +1,581 @@
// 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_comment_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');
ConsultantCommentModel _$ConsultantCommentModelFromJson(
Map<String, dynamic> json) {
return _ConsultantCommentModel.fromJson(json);
}
/// @nodoc
mixin _$ConsultantCommentModel {
String? get comment => throw _privateConstructorUsedError;
@JsonKey(name: 'meals')
Map<String, MealCommentModel> get mealsComments =>
throw _privateConstructorUsedError;
List<PortionCommentModel> get portions => throw _privateConstructorUsedError;
/// Serializes this ConsultantCommentModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of ConsultantCommentModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ConsultantCommentModelCopyWith<ConsultantCommentModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ConsultantCommentModelCopyWith<$Res> {
factory $ConsultantCommentModelCopyWith(ConsultantCommentModel value,
$Res Function(ConsultantCommentModel) then) =
_$ConsultantCommentModelCopyWithImpl<$Res, ConsultantCommentModel>;
@useResult
$Res call(
{String? comment,
@JsonKey(name: 'meals') Map<String, MealCommentModel> mealsComments,
List<PortionCommentModel> portions});
}
/// @nodoc
class _$ConsultantCommentModelCopyWithImpl<$Res,
$Val extends ConsultantCommentModel>
implements $ConsultantCommentModelCopyWith<$Res> {
_$ConsultantCommentModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of ConsultantCommentModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? comment = freezed,
Object? mealsComments = null,
Object? portions = null,
}) {
return _then(_value.copyWith(
comment: freezed == comment
? _value.comment
: comment // ignore: cast_nullable_to_non_nullable
as String?,
mealsComments: null == mealsComments
? _value.mealsComments
: mealsComments // ignore: cast_nullable_to_non_nullable
as Map<String, MealCommentModel>,
portions: null == portions
? _value.portions
: portions // ignore: cast_nullable_to_non_nullable
as List<PortionCommentModel>,
) as $Val);
}
}
/// @nodoc
abstract class _$$ConsultantCommentModelImplCopyWith<$Res>
implements $ConsultantCommentModelCopyWith<$Res> {
factory _$$ConsultantCommentModelImplCopyWith(
_$ConsultantCommentModelImpl value,
$Res Function(_$ConsultantCommentModelImpl) then) =
__$$ConsultantCommentModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{String? comment,
@JsonKey(name: 'meals') Map<String, MealCommentModel> mealsComments,
List<PortionCommentModel> portions});
}
/// @nodoc
class __$$ConsultantCommentModelImplCopyWithImpl<$Res>
extends _$ConsultantCommentModelCopyWithImpl<$Res,
_$ConsultantCommentModelImpl>
implements _$$ConsultantCommentModelImplCopyWith<$Res> {
__$$ConsultantCommentModelImplCopyWithImpl(
_$ConsultantCommentModelImpl _value,
$Res Function(_$ConsultantCommentModelImpl) _then)
: super(_value, _then);
/// Create a copy of ConsultantCommentModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? comment = freezed,
Object? mealsComments = null,
Object? portions = null,
}) {
return _then(_$ConsultantCommentModelImpl(
comment: freezed == comment
? _value.comment
: comment // ignore: cast_nullable_to_non_nullable
as String?,
mealsComments: null == mealsComments
? _value._mealsComments
: mealsComments // ignore: cast_nullable_to_non_nullable
as Map<String, MealCommentModel>,
portions: null == portions
? _value._portions
: portions // ignore: cast_nullable_to_non_nullable
as List<PortionCommentModel>,
));
}
}
/// @nodoc
@JsonSerializable()
class _$ConsultantCommentModelImpl implements _ConsultantCommentModel {
_$ConsultantCommentModelImpl(
{this.comment,
@JsonKey(name: 'meals')
final Map<String, MealCommentModel> mealsComments = const {},
final List<PortionCommentModel> portions = const []})
: _mealsComments = mealsComments,
_portions = portions;
factory _$ConsultantCommentModelImpl.fromJson(Map<String, dynamic> json) =>
_$$ConsultantCommentModelImplFromJson(json);
@override
final String? comment;
final Map<String, MealCommentModel> _mealsComments;
@override
@JsonKey(name: 'meals')
Map<String, MealCommentModel> get mealsComments {
if (_mealsComments is EqualUnmodifiableMapView) return _mealsComments;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_mealsComments);
}
final List<PortionCommentModel> _portions;
@override
@JsonKey()
List<PortionCommentModel> get portions {
if (_portions is EqualUnmodifiableListView) return _portions;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_portions);
}
@override
String toString() {
return 'ConsultantCommentModel(comment: $comment, mealsComments: $mealsComments, portions: $portions)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ConsultantCommentModelImpl &&
(identical(other.comment, comment) || other.comment == comment) &&
const DeepCollectionEquality()
.equals(other._mealsComments, _mealsComments) &&
const DeepCollectionEquality().equals(other._portions, _portions));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType,
comment,
const DeepCollectionEquality().hash(_mealsComments),
const DeepCollectionEquality().hash(_portions));
/// Create a copy of ConsultantCommentModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ConsultantCommentModelImplCopyWith<_$ConsultantCommentModelImpl>
get copyWith => __$$ConsultantCommentModelImplCopyWithImpl<
_$ConsultantCommentModelImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$ConsultantCommentModelImplToJson(
this,
);
}
}
abstract class _ConsultantCommentModel implements ConsultantCommentModel {
factory _ConsultantCommentModel(
{final String? comment,
@JsonKey(name: 'meals') final Map<String, MealCommentModel> mealsComments,
final List<PortionCommentModel> portions}) = _$ConsultantCommentModelImpl;
factory _ConsultantCommentModel.fromJson(Map<String, dynamic> json) =
_$ConsultantCommentModelImpl.fromJson;
@override
String? get comment;
@override
@JsonKey(name: 'meals')
Map<String, MealCommentModel> get mealsComments;
@override
List<PortionCommentModel> get portions;
/// Create a copy of ConsultantCommentModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ConsultantCommentModelImplCopyWith<_$ConsultantCommentModelImpl>
get copyWith => throw _privateConstructorUsedError;
}
PortionCommentModel _$PortionCommentModelFromJson(Map<String, dynamic> json) {
return _PortionCommentModel.fromJson(json);
}
/// @nodoc
mixin _$PortionCommentModel {
String get comment => throw _privateConstructorUsedError;
int get id => throw _privateConstructorUsedError;
/// Serializes this PortionCommentModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of PortionCommentModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$PortionCommentModelCopyWith<PortionCommentModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $PortionCommentModelCopyWith<$Res> {
factory $PortionCommentModelCopyWith(
PortionCommentModel value, $Res Function(PortionCommentModel) then) =
_$PortionCommentModelCopyWithImpl<$Res, PortionCommentModel>;
@useResult
$Res call({String comment, int id});
}
/// @nodoc
class _$PortionCommentModelCopyWithImpl<$Res, $Val extends PortionCommentModel>
implements $PortionCommentModelCopyWith<$Res> {
_$PortionCommentModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of PortionCommentModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? comment = null,
Object? id = null,
}) {
return _then(_value.copyWith(
comment: null == comment
? _value.comment
: comment // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
) as $Val);
}
}
/// @nodoc
abstract class _$$PortionCommentModelImplCopyWith<$Res>
implements $PortionCommentModelCopyWith<$Res> {
factory _$$PortionCommentModelImplCopyWith(_$PortionCommentModelImpl value,
$Res Function(_$PortionCommentModelImpl) then) =
__$$PortionCommentModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String comment, int id});
}
/// @nodoc
class __$$PortionCommentModelImplCopyWithImpl<$Res>
extends _$PortionCommentModelCopyWithImpl<$Res, _$PortionCommentModelImpl>
implements _$$PortionCommentModelImplCopyWith<$Res> {
__$$PortionCommentModelImplCopyWithImpl(_$PortionCommentModelImpl _value,
$Res Function(_$PortionCommentModelImpl) _then)
: super(_value, _then);
/// Create a copy of PortionCommentModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? comment = null,
Object? id = null,
}) {
return _then(_$PortionCommentModelImpl(
comment: null == comment
? _value.comment
: comment // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// @nodoc
@JsonSerializable()
class _$PortionCommentModelImpl implements _PortionCommentModel {
_$PortionCommentModelImpl({required this.comment, required this.id});
factory _$PortionCommentModelImpl.fromJson(Map<String, dynamic> json) =>
_$$PortionCommentModelImplFromJson(json);
@override
final String comment;
@override
final int id;
@override
String toString() {
return 'PortionCommentModel(comment: $comment, id: $id)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$PortionCommentModelImpl &&
(identical(other.comment, comment) || other.comment == comment) &&
(identical(other.id, id) || other.id == id));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, comment, id);
/// Create a copy of PortionCommentModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$PortionCommentModelImplCopyWith<_$PortionCommentModelImpl> get copyWith =>
__$$PortionCommentModelImplCopyWithImpl<_$PortionCommentModelImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$PortionCommentModelImplToJson(
this,
);
}
}
abstract class _PortionCommentModel implements PortionCommentModel {
factory _PortionCommentModel(
{required final String comment,
required final int id}) = _$PortionCommentModelImpl;
factory _PortionCommentModel.fromJson(Map<String, dynamic> json) =
_$PortionCommentModelImpl.fromJson;
@override
String get comment;
@override
int get id;
/// Create a copy of PortionCommentModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$PortionCommentModelImplCopyWith<_$PortionCommentModelImpl> get copyWith =>
throw _privateConstructorUsedError;
}
MealCommentModel _$MealCommentModelFromJson(Map<String, dynamic> json) {
return _MealCommentModel.fromJson(json);
}
/// @nodoc
mixin _$MealCommentModel {
String get comment => throw _privateConstructorUsedError;
@JsonKey(name: 'type_no')
int get typeNumber => throw _privateConstructorUsedError;
/// Serializes this MealCommentModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of MealCommentModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$MealCommentModelCopyWith<MealCommentModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $MealCommentModelCopyWith<$Res> {
factory $MealCommentModelCopyWith(
MealCommentModel value, $Res Function(MealCommentModel) then) =
_$MealCommentModelCopyWithImpl<$Res, MealCommentModel>;
@useResult
$Res call({String comment, @JsonKey(name: 'type_no') int typeNumber});
}
/// @nodoc
class _$MealCommentModelCopyWithImpl<$Res, $Val extends MealCommentModel>
implements $MealCommentModelCopyWith<$Res> {
_$MealCommentModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of MealCommentModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? comment = null,
Object? typeNumber = null,
}) {
return _then(_value.copyWith(
comment: null == comment
? _value.comment
: comment // ignore: cast_nullable_to_non_nullable
as String,
typeNumber: null == typeNumber
? _value.typeNumber
: typeNumber // ignore: cast_nullable_to_non_nullable
as int,
) as $Val);
}
}
/// @nodoc
abstract class _$$MealCommentModelImplCopyWith<$Res>
implements $MealCommentModelCopyWith<$Res> {
factory _$$MealCommentModelImplCopyWith(_$MealCommentModelImpl value,
$Res Function(_$MealCommentModelImpl) then) =
__$$MealCommentModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String comment, @JsonKey(name: 'type_no') int typeNumber});
}
/// @nodoc
class __$$MealCommentModelImplCopyWithImpl<$Res>
extends _$MealCommentModelCopyWithImpl<$Res, _$MealCommentModelImpl>
implements _$$MealCommentModelImplCopyWith<$Res> {
__$$MealCommentModelImplCopyWithImpl(_$MealCommentModelImpl _value,
$Res Function(_$MealCommentModelImpl) _then)
: super(_value, _then);
/// Create a copy of MealCommentModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? comment = null,
Object? typeNumber = null,
}) {
return _then(_$MealCommentModelImpl(
comment: null == comment
? _value.comment
: comment // ignore: cast_nullable_to_non_nullable
as String,
typeNumber: null == typeNumber
? _value.typeNumber
: typeNumber // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// @nodoc
@JsonSerializable()
class _$MealCommentModelImpl implements _MealCommentModel {
_$MealCommentModelImpl(
{required this.comment,
@JsonKey(name: 'type_no') required this.typeNumber});
factory _$MealCommentModelImpl.fromJson(Map<String, dynamic> json) =>
_$$MealCommentModelImplFromJson(json);
@override
final String comment;
@override
@JsonKey(name: 'type_no')
final int typeNumber;
@override
String toString() {
return 'MealCommentModel(comment: $comment, typeNumber: $typeNumber)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$MealCommentModelImpl &&
(identical(other.comment, comment) || other.comment == comment) &&
(identical(other.typeNumber, typeNumber) ||
other.typeNumber == typeNumber));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, comment, typeNumber);
/// Create a copy of MealCommentModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$MealCommentModelImplCopyWith<_$MealCommentModelImpl> get copyWith =>
__$$MealCommentModelImplCopyWithImpl<_$MealCommentModelImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$MealCommentModelImplToJson(
this,
);
}
}
abstract class _MealCommentModel implements MealCommentModel {
factory _MealCommentModel(
{required final String comment,
@JsonKey(name: 'type_no') required final int typeNumber}) =
_$MealCommentModelImpl;
factory _MealCommentModel.fromJson(Map<String, dynamic> json) =
_$MealCommentModelImpl.fromJson;
@override
String get comment;
@override
@JsonKey(name: 'type_no')
int get typeNumber;
/// Create a copy of MealCommentModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$MealCommentModelImplCopyWith<_$MealCommentModelImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -0,0 +1,59 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'consultant_comment_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$ConsultantCommentModelImpl _$$ConsultantCommentModelImplFromJson(
Map<String, dynamic> json) =>
_$ConsultantCommentModelImpl(
comment: json['comment'] as String?,
mealsComments: (json['meals'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(
k, MealCommentModel.fromJson(e as Map<String, dynamic>)),
) ??
const {},
portions: (json['portions'] as List<dynamic>?)
?.map((e) =>
PortionCommentModel.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
);
Map<String, dynamic> _$$ConsultantCommentModelImplToJson(
_$ConsultantCommentModelImpl instance) =>
<String, dynamic>{
'comment': instance.comment,
'meals': instance.mealsComments,
'portions': instance.portions,
};
_$PortionCommentModelImpl _$$PortionCommentModelImplFromJson(
Map<String, dynamic> json) =>
_$PortionCommentModelImpl(
comment: json['comment'] as String,
id: (json['id'] as num).toInt(),
);
Map<String, dynamic> _$$PortionCommentModelImplToJson(
_$PortionCommentModelImpl instance) =>
<String, dynamic>{
'comment': instance.comment,
'id': instance.id,
};
_$MealCommentModelImpl _$$MealCommentModelImplFromJson(
Map<String, dynamic> json) =>
_$MealCommentModelImpl(
comment: json['comment'] as String,
typeNumber: (json['type_no'] as num).toInt(),
);
Map<String, dynamic> _$$MealCommentModelImplToJson(
_$MealCommentModelImpl instance) =>
<String, dynamic>{
'comment': instance.comment,
'type_no': instance.typeNumber,
};

View File

@@ -0,0 +1,53 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'consultant_review_model.freezed.dart';
part 'consultant_review_model.g.dart';
@freezed
class ConsultantReviewModel with _$ConsultantReviewModel {
factory ConsultantReviewModel({
required String planName,
required int planDuration,
required int currentPlanDay,
required ConsultantComments consultantComments,
}) = _ConsultantReviewModel;
factory ConsultantReviewModel.fromJson(Map<String, dynamic> json) => _$ConsultantReviewModelFromJson(json);
}
@freezed
class ConsultantComments with _$ConsultantComments {
factory ConsultantComments({
required String? dayComment,
required List<MealComment> mealsComments,
required List<PortionComment> portionsComments,
}) = _ConsultantComments;
factory ConsultantComments.fromJson(Map<String, dynamic> json) => _$ConsultantCommentsFromJson(json);
}
@freezed
class MealComment with _$MealComment {
factory MealComment({
required String comment,
}) = _MealComment;
factory MealComment.fromJson(Map<String, dynamic> json) => _$MealCommentFromJson(json);
}
@freezed
class PortionComment with _$PortionComment {
factory PortionComment({
@JsonKey(name: 'commentable_id') required int portionId,
required String comment,
}) = _PortionComment;
factory PortionComment.fromJson(Map<String, dynamic> json) => _$PortionCommentFromJson(json);
}

View File

@@ -0,0 +1,796 @@
// 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;
/// 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});
}
/// @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,
}) {
return _then(_value.copyWith(
comment: null == comment
? _value.comment
: comment // ignore: cast_nullable_to_non_nullable
as String,
) 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});
}
/// @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,
}) {
return _then(_$MealCommentImpl(
comment: null == comment
? _value.comment
: comment // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$MealCommentImpl implements _MealComment {
_$MealCommentImpl({required this.comment});
factory _$MealCommentImpl.fromJson(Map<String, dynamic> json) =>
_$$MealCommentImplFromJson(json);
@override
final String comment;
@override
String toString() {
return 'MealComment(comment: $comment)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$MealCommentImpl &&
(identical(other.comment, comment) || other.comment == comment));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, comment);
/// 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}) = _$MealCommentImpl;
factory _MealComment.fromJson(Map<String, dynamic> json) =
_$MealCommentImpl.fromJson;
@override
String get comment;
/// 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: 'commentable_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: 'commentable_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: 'commentable_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: 'commentable_id') required this.portionId,
required this.comment});
factory _$PortionCommentImpl.fromJson(Map<String, dynamic> json) =>
_$$PortionCommentImplFromJson(json);
@override
@JsonKey(name: 'commentable_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: 'commentable_id') required final int portionId,
required final String comment}) = _$PortionCommentImpl;
factory _PortionComment.fromJson(Map<String, dynamic> json) =
_$PortionCommentImpl.fromJson;
@override
@JsonKey(name: 'commentable_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;
}

View File

@@ -0,0 +1,69 @@
// 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,
);
Map<String, dynamic> _$$MealCommentImplToJson(_$MealCommentImpl instance) =>
<String, dynamic>{
'comment': instance.comment,
};
_$PortionCommentImpl _$$PortionCommentImplFromJson(Map<String, dynamic> json) =>
_$PortionCommentImpl(
portionId: (json['commentable_id'] as num).toInt(),
comment: json['comment'] as String,
);
Map<String, dynamic> _$$PortionCommentImplToJson(
_$PortionCommentImpl instance) =>
<String, dynamic>{
'commentable_id': instance.portionId,
'comment': instance.comment,
};

View File

@@ -1,3 +1,4 @@
import 'package:eatvio_app/features/consultations/models/consultation_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'diary_day_model.freezed.dart';
@@ -39,6 +40,7 @@ class DiaryDayModel with _$DiaryDayModel {
@JsonKey(name: "comment") required String comment,
@JsonKey(name: "state") required TypeOfDiaryModel typeOfDiary,
@JsonKey(name: "meals") required List<MealModel> meals,
@JsonKey(name: "chosen_plan_id") required int? chosenPlanId,
}) = _DiaryDayModel;
factory DiaryDayModel.fromJson(Map<String, dynamic> json) => _$DiaryDayModelFromJson(json);
@@ -91,6 +93,7 @@ class TypeOfDiaryModel with _$TypeOfDiaryModel {
const factory TypeOfDiaryModel({
@JsonKey(name: "has_chosen_diet") required bool hasChosenDiet,
@JsonKey(name: "has_chosen_plan") required bool hasChosenPlan,
@JsonKey(name: "last_state") required DiaryStateEnum diaryState,
}) = _TypeOfDiaryModel;
factory TypeOfDiaryModel.fromJson(Map<String, dynamic> json) => _$TypeOfDiaryModelFromJson(json);

View File

@@ -209,6 +209,8 @@ mixin _$DiaryDayModel {
TypeOfDiaryModel get typeOfDiary => throw _privateConstructorUsedError;
@JsonKey(name: "meals")
List<MealModel> get meals => throw _privateConstructorUsedError;
@JsonKey(name: "chosen_plan_id")
int? get chosenPlanId => throw _privateConstructorUsedError;
/// Serializes this DiaryDayModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@@ -234,7 +236,8 @@ abstract class $DiaryDayModelCopyWith<$Res> {
@JsonKey(name: "closed") int closed,
@JsonKey(name: "comment") String comment,
@JsonKey(name: "state") TypeOfDiaryModel typeOfDiary,
@JsonKey(name: "meals") List<MealModel> meals});
@JsonKey(name: "meals") List<MealModel> meals,
@JsonKey(name: "chosen_plan_id") int? chosenPlanId});
$TypeOfDiaryModelCopyWith<$Res> get typeOfDiary;
}
@@ -262,6 +265,7 @@ class _$DiaryDayModelCopyWithImpl<$Res, $Val extends DiaryDayModel>
Object? comment = null,
Object? typeOfDiary = null,
Object? meals = null,
Object? chosenPlanId = freezed,
}) {
return _then(_value.copyWith(
id: null == id
@@ -296,6 +300,10 @@ class _$DiaryDayModelCopyWithImpl<$Res, $Val extends DiaryDayModel>
? _value.meals
: meals // ignore: cast_nullable_to_non_nullable
as List<MealModel>,
chosenPlanId: freezed == chosenPlanId
? _value.chosenPlanId
: chosenPlanId // ignore: cast_nullable_to_non_nullable
as int?,
) as $Val);
}
@@ -326,7 +334,8 @@ abstract class _$$DiaryDayModelImplCopyWith<$Res>
@JsonKey(name: "closed") int closed,
@JsonKey(name: "comment") String comment,
@JsonKey(name: "state") TypeOfDiaryModel typeOfDiary,
@JsonKey(name: "meals") List<MealModel> meals});
@JsonKey(name: "meals") List<MealModel> meals,
@JsonKey(name: "chosen_plan_id") int? chosenPlanId});
@override
$TypeOfDiaryModelCopyWith<$Res> get typeOfDiary;
@@ -353,6 +362,7 @@ class __$$DiaryDayModelImplCopyWithImpl<$Res>
Object? comment = null,
Object? typeOfDiary = null,
Object? meals = null,
Object? chosenPlanId = freezed,
}) {
return _then(_$DiaryDayModelImpl(
id: null == id
@@ -387,6 +397,10 @@ class __$$DiaryDayModelImplCopyWithImpl<$Res>
? _value._meals
: meals // ignore: cast_nullable_to_non_nullable
as List<MealModel>,
chosenPlanId: freezed == chosenPlanId
? _value.chosenPlanId
: chosenPlanId // ignore: cast_nullable_to_non_nullable
as int?,
));
}
}
@@ -402,7 +416,8 @@ class _$DiaryDayModelImpl implements _DiaryDayModel {
@JsonKey(name: "closed") required this.closed,
@JsonKey(name: "comment") required this.comment,
@JsonKey(name: "state") required this.typeOfDiary,
@JsonKey(name: "meals") required final List<MealModel> meals})
@JsonKey(name: "meals") required final List<MealModel> meals,
@JsonKey(name: "chosen_plan_id") required this.chosenPlanId})
: _meals = meals;
factory _$DiaryDayModelImpl.fromJson(Map<String, dynamic> json) =>
@@ -438,9 +453,13 @@ class _$DiaryDayModelImpl implements _DiaryDayModel {
return EqualUnmodifiableListView(_meals);
}
@override
@JsonKey(name: "chosen_plan_id")
final int? chosenPlanId;
@override
String toString() {
return 'DiaryDayModel(id: $id, date: $date, wakeup: $wakeup, retire: $retire, closed: $closed, comment: $comment, typeOfDiary: $typeOfDiary, meals: $meals)';
return 'DiaryDayModel(id: $id, date: $date, wakeup: $wakeup, retire: $retire, closed: $closed, comment: $comment, typeOfDiary: $typeOfDiary, meals: $meals, chosenPlanId: $chosenPlanId)';
}
@override
@@ -456,13 +475,24 @@ class _$DiaryDayModelImpl implements _DiaryDayModel {
(identical(other.comment, comment) || other.comment == comment) &&
(identical(other.typeOfDiary, typeOfDiary) ||
other.typeOfDiary == typeOfDiary) &&
const DeepCollectionEquality().equals(other._meals, _meals));
const DeepCollectionEquality().equals(other._meals, _meals) &&
(identical(other.chosenPlanId, chosenPlanId) ||
other.chosenPlanId == chosenPlanId));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, id, date, wakeup, retire, closed,
comment, typeOfDiary, const DeepCollectionEquality().hash(_meals));
int get hashCode => Object.hash(
runtimeType,
id,
date,
wakeup,
retire,
closed,
comment,
typeOfDiary,
const DeepCollectionEquality().hash(_meals),
chosenPlanId);
/// Create a copy of DiaryDayModel
/// with the given fields replaced by the non-null parameter values.
@@ -489,7 +519,8 @@ abstract class _DiaryDayModel implements DiaryDayModel {
@JsonKey(name: "closed") required final int closed,
@JsonKey(name: "comment") required final String comment,
@JsonKey(name: "state") required final TypeOfDiaryModel typeOfDiary,
@JsonKey(name: "meals") required final List<MealModel> meals}) =
@JsonKey(name: "meals") required final List<MealModel> meals,
@JsonKey(name: "chosen_plan_id") required final int? chosenPlanId}) =
_$DiaryDayModelImpl;
factory _DiaryDayModel.fromJson(Map<String, dynamic> json) =
@@ -519,6 +550,9 @@ abstract class _DiaryDayModel implements DiaryDayModel {
@override
@JsonKey(name: "meals")
List<MealModel> get meals;
@override
@JsonKey(name: "chosen_plan_id")
int? get chosenPlanId;
/// Create a copy of DiaryDayModel
/// with the given fields replaced by the non-null parameter values.
@@ -1387,6 +1421,8 @@ mixin _$TypeOfDiaryModel {
bool get hasChosenDiet => throw _privateConstructorUsedError;
@JsonKey(name: "has_chosen_plan")
bool get hasChosenPlan => throw _privateConstructorUsedError;
@JsonKey(name: "last_state")
DiaryStateEnum get diaryState => throw _privateConstructorUsedError;
/// Serializes this TypeOfDiaryModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@@ -1406,7 +1442,8 @@ abstract class $TypeOfDiaryModelCopyWith<$Res> {
@useResult
$Res call(
{@JsonKey(name: "has_chosen_diet") bool hasChosenDiet,
@JsonKey(name: "has_chosen_plan") bool hasChosenPlan});
@JsonKey(name: "has_chosen_plan") bool hasChosenPlan,
@JsonKey(name: "last_state") DiaryStateEnum diaryState});
}
/// @nodoc
@@ -1426,6 +1463,7 @@ class _$TypeOfDiaryModelCopyWithImpl<$Res, $Val extends TypeOfDiaryModel>
$Res call({
Object? hasChosenDiet = null,
Object? hasChosenPlan = null,
Object? diaryState = null,
}) {
return _then(_value.copyWith(
hasChosenDiet: null == hasChosenDiet
@@ -1436,6 +1474,10 @@ class _$TypeOfDiaryModelCopyWithImpl<$Res, $Val extends TypeOfDiaryModel>
? _value.hasChosenPlan
: hasChosenPlan // ignore: cast_nullable_to_non_nullable
as bool,
diaryState: null == diaryState
? _value.diaryState
: diaryState // ignore: cast_nullable_to_non_nullable
as DiaryStateEnum,
) as $Val);
}
}
@@ -1450,7 +1492,8 @@ abstract class _$$TypeOfDiaryModelImplCopyWith<$Res>
@useResult
$Res call(
{@JsonKey(name: "has_chosen_diet") bool hasChosenDiet,
@JsonKey(name: "has_chosen_plan") bool hasChosenPlan});
@JsonKey(name: "has_chosen_plan") bool hasChosenPlan,
@JsonKey(name: "last_state") DiaryStateEnum diaryState});
}
/// @nodoc
@@ -1468,6 +1511,7 @@ class __$$TypeOfDiaryModelImplCopyWithImpl<$Res>
$Res call({
Object? hasChosenDiet = null,
Object? hasChosenPlan = null,
Object? diaryState = null,
}) {
return _then(_$TypeOfDiaryModelImpl(
hasChosenDiet: null == hasChosenDiet
@@ -1478,6 +1522,10 @@ class __$$TypeOfDiaryModelImplCopyWithImpl<$Res>
? _value.hasChosenPlan
: hasChosenPlan // ignore: cast_nullable_to_non_nullable
as bool,
diaryState: null == diaryState
? _value.diaryState
: diaryState // ignore: cast_nullable_to_non_nullable
as DiaryStateEnum,
));
}
}
@@ -1487,7 +1535,8 @@ class __$$TypeOfDiaryModelImplCopyWithImpl<$Res>
class _$TypeOfDiaryModelImpl implements _TypeOfDiaryModel {
const _$TypeOfDiaryModelImpl(
{@JsonKey(name: "has_chosen_diet") required this.hasChosenDiet,
@JsonKey(name: "has_chosen_plan") required this.hasChosenPlan});
@JsonKey(name: "has_chosen_plan") required this.hasChosenPlan,
@JsonKey(name: "last_state") required this.diaryState});
factory _$TypeOfDiaryModelImpl.fromJson(Map<String, dynamic> json) =>
_$$TypeOfDiaryModelImplFromJson(json);
@@ -1498,10 +1547,13 @@ class _$TypeOfDiaryModelImpl implements _TypeOfDiaryModel {
@override
@JsonKey(name: "has_chosen_plan")
final bool hasChosenPlan;
@override
@JsonKey(name: "last_state")
final DiaryStateEnum diaryState;
@override
String toString() {
return 'TypeOfDiaryModel(hasChosenDiet: $hasChosenDiet, hasChosenPlan: $hasChosenPlan)';
return 'TypeOfDiaryModel(hasChosenDiet: $hasChosenDiet, hasChosenPlan: $hasChosenPlan, diaryState: $diaryState)';
}
@override
@@ -1512,12 +1564,15 @@ class _$TypeOfDiaryModelImpl implements _TypeOfDiaryModel {
(identical(other.hasChosenDiet, hasChosenDiet) ||
other.hasChosenDiet == hasChosenDiet) &&
(identical(other.hasChosenPlan, hasChosenPlan) ||
other.hasChosenPlan == hasChosenPlan));
other.hasChosenPlan == hasChosenPlan) &&
(identical(other.diaryState, diaryState) ||
other.diaryState == diaryState));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, hasChosenDiet, hasChosenPlan);
int get hashCode =>
Object.hash(runtimeType, hasChosenDiet, hasChosenPlan, diaryState);
/// Create a copy of TypeOfDiaryModel
/// with the given fields replaced by the non-null parameter values.
@@ -1539,8 +1594,9 @@ class _$TypeOfDiaryModelImpl implements _TypeOfDiaryModel {
abstract class _TypeOfDiaryModel implements TypeOfDiaryModel {
const factory _TypeOfDiaryModel(
{@JsonKey(name: "has_chosen_diet") required final bool hasChosenDiet,
@JsonKey(name: "has_chosen_plan")
required final bool hasChosenPlan}) = _$TypeOfDiaryModelImpl;
@JsonKey(name: "has_chosen_plan") required final bool hasChosenPlan,
@JsonKey(name: "last_state")
required final DiaryStateEnum diaryState}) = _$TypeOfDiaryModelImpl;
factory _TypeOfDiaryModel.fromJson(Map<String, dynamic> json) =
_$TypeOfDiaryModelImpl.fromJson;
@@ -1551,6 +1607,9 @@ abstract class _TypeOfDiaryModel implements TypeOfDiaryModel {
@override
@JsonKey(name: "has_chosen_plan")
bool get hasChosenPlan;
@override
@JsonKey(name: "last_state")
DiaryStateEnum get diaryState;
/// Create a copy of TypeOfDiaryModel
/// with the given fields replaced by the non-null parameter values.

View File

@@ -31,6 +31,7 @@ _$DiaryDayModelImpl _$$DiaryDayModelImplFromJson(Map<String, dynamic> json) =>
meals: (json['meals'] as List<dynamic>)
.map((e) => MealModel.fromJson(e as Map<String, dynamic>))
.toList(),
chosenPlanId: (json['chosen_plan_id'] as num?)?.toInt(),
);
Map<String, dynamic> _$$DiaryDayModelImplToJson(_$DiaryDayModelImpl instance) =>
@@ -43,6 +44,7 @@ Map<String, dynamic> _$$DiaryDayModelImplToJson(_$DiaryDayModelImpl instance) =>
'comment': instance.comment,
'state': instance.typeOfDiary,
'meals': instance.meals,
'chosen_plan_id': instance.chosenPlanId,
};
_$MealModelImpl _$$MealModelImplFromJson(Map<String, dynamic> json) =>
@@ -127,6 +129,7 @@ _$TypeOfDiaryModelImpl _$$TypeOfDiaryModelImplFromJson(
_$TypeOfDiaryModelImpl(
hasChosenDiet: json['has_chosen_diet'] as bool,
hasChosenPlan: json['has_chosen_plan'] as bool,
diaryState: $enumDecode(_$DiaryStateEnumEnumMap, json['last_state']),
);
Map<String, dynamic> _$$TypeOfDiaryModelImplToJson(
@@ -134,8 +137,20 @@ Map<String, dynamic> _$$TypeOfDiaryModelImplToJson(
<String, dynamic>{
'has_chosen_diet': instance.hasChosenDiet,
'has_chosen_plan': instance.hasChosenPlan,
'last_state': _$DiaryStateEnumEnumMap[instance.diaryState]!,
};
const _$DiaryStateEnumEnumMap = {
DiaryStateEnum.init: 'init',
DiaryStateEnum.started: 'started',
DiaryStateEnum.finished: 'finished',
DiaryStateEnum.commented: 'commented',
DiaryStateEnum.reviewedByUser: 'reviewed_by_user',
DiaryStateEnum.lastDayStarted: 'last_day_started',
DiaryStateEnum.lastDayFinished: 'last_day_finished',
DiaryStateEnum.lastDayCommented: 'last_day_commented',
};
_$ProductInMealImpl _$$ProductInMealImplFromJson(Map<String, dynamic> json) =>
_$ProductInMealImpl(
productName: json['product_name'] as String,

View File

@@ -0,0 +1,80 @@
import 'dart:developer';
import 'package:eatvio_app/features/diary/models/consultant_comment_model.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_state.dart';
import 'package:eatvio_app/features/diary/repositories/diary_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class ConsultantCommentsCubit extends Cubit<ConsultantCommentsState> {
final DiaryRepository diaryRepository;
ConsultantCommentsCubit({required this.diaryRepository})
: super(
ConsultantCommentsState(
commentsModel: ConsultantCommentModel(
comment: '',
),
),
);
void setComment(String comment) {
emit(
state.copyWith(
commentsModel: state.commentsModel.copyWith(comment: comment),
dayCommentNeedsToBeFilled: comment.length < 3,
),
);
}
void setMealComment({required int mealNumber, required String comment}) {
if (comment.isEmpty) {
final mealsComments = Map<String, MealCommentModel>.from(state.commentsModel.mealsComments);
mealsComments.remove(mealNumber.toString());
emit(state.copyWith.commentsModel(mealsComments: mealsComments));
return;
}
final mealsComments = Map<String, MealCommentModel>.from(state.commentsModel.mealsComments);
mealsComments[mealNumber.toString()] = MealCommentModel(comment: comment, typeNumber: mealNumber);
emit(state.copyWith.commentsModel(mealsComments: mealsComments));
}
void addPortionComment(PortionCommentModel portionComment) {
final portions = [...state.commentsModel.portions];
portions.add(portionComment);
emit(state.copyWith.commentsModel(portions: portions));
}
void updatePortionComment(PortionCommentModel portionCommentModel) {
if (portionCommentModel.comment.isEmpty) {
final portions = [...state.commentsModel.portions];
final updatedPortions = portions.where((portion) => portion.id != portionCommentModel.id).toList();
emit(state.copyWith.commentsModel(portions: updatedPortions));
return;
}
final portions = [...state.commentsModel.portions];
final updatedPortions = portions.map((portion) {
if (portion.id == portionCommentModel.id) {
return portionCommentModel;
}
return portion;
}).toList();
emit(state.copyWith.commentsModel(portions: updatedPortions));
}
Future<void> submitComments(int diaryId) async {
try {
if (state.commentsModel.comment == null || state.commentsModel.comment!.isEmpty || state.commentsModel.comment!.length < 3) {
emit(state.copyWith(dayCommentNeedsToBeFilled: true));
return;
}
if (state.commentsModel.mealsComments.values.any((element) => element.comment.length < 3)) {
return;
}
if (state.commentsModel.portions.any((element) => element.comment.length < 3)) {
return;
}
await diaryRepository.commentUserDay(diaryId: diaryId, commentModel: state.commentsModel);
} catch (e, st) {
log(e.toString(), stackTrace: st);
}
}
}

View File

@@ -0,0 +1,15 @@
import 'package:eatvio_app/features/diary/models/consultant_comment_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'consultant_comments_state.freezed.dart';
@freezed
class ConsultantCommentsState with _$ConsultantCommentsState {
factory ConsultantCommentsState({
required ConsultantCommentModel commentsModel,
@Default(false) bool dayCommentNeedsToBeFilled
}) = _ConsultantCommentsState;
}

View File

@@ -0,0 +1,190 @@
// 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_comments_state.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');
/// @nodoc
mixin _$ConsultantCommentsState {
ConsultantCommentModel get commentsModel =>
throw _privateConstructorUsedError;
bool get dayCommentNeedsToBeFilled => throw _privateConstructorUsedError;
/// Create a copy of ConsultantCommentsState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ConsultantCommentsStateCopyWith<ConsultantCommentsState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ConsultantCommentsStateCopyWith<$Res> {
factory $ConsultantCommentsStateCopyWith(ConsultantCommentsState value,
$Res Function(ConsultantCommentsState) then) =
_$ConsultantCommentsStateCopyWithImpl<$Res, ConsultantCommentsState>;
@useResult
$Res call(
{ConsultantCommentModel commentsModel, bool dayCommentNeedsToBeFilled});
$ConsultantCommentModelCopyWith<$Res> get commentsModel;
}
/// @nodoc
class _$ConsultantCommentsStateCopyWithImpl<$Res,
$Val extends ConsultantCommentsState>
implements $ConsultantCommentsStateCopyWith<$Res> {
_$ConsultantCommentsStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of ConsultantCommentsState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? commentsModel = null,
Object? dayCommentNeedsToBeFilled = null,
}) {
return _then(_value.copyWith(
commentsModel: null == commentsModel
? _value.commentsModel
: commentsModel // ignore: cast_nullable_to_non_nullable
as ConsultantCommentModel,
dayCommentNeedsToBeFilled: null == dayCommentNeedsToBeFilled
? _value.dayCommentNeedsToBeFilled
: dayCommentNeedsToBeFilled // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val);
}
/// Create a copy of ConsultantCommentsState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ConsultantCommentModelCopyWith<$Res> get commentsModel {
return $ConsultantCommentModelCopyWith<$Res>(_value.commentsModel, (value) {
return _then(_value.copyWith(commentsModel: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$ConsultantCommentsStateImplCopyWith<$Res>
implements $ConsultantCommentsStateCopyWith<$Res> {
factory _$$ConsultantCommentsStateImplCopyWith(
_$ConsultantCommentsStateImpl value,
$Res Function(_$ConsultantCommentsStateImpl) then) =
__$$ConsultantCommentsStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{ConsultantCommentModel commentsModel, bool dayCommentNeedsToBeFilled});
@override
$ConsultantCommentModelCopyWith<$Res> get commentsModel;
}
/// @nodoc
class __$$ConsultantCommentsStateImplCopyWithImpl<$Res>
extends _$ConsultantCommentsStateCopyWithImpl<$Res,
_$ConsultantCommentsStateImpl>
implements _$$ConsultantCommentsStateImplCopyWith<$Res> {
__$$ConsultantCommentsStateImplCopyWithImpl(
_$ConsultantCommentsStateImpl _value,
$Res Function(_$ConsultantCommentsStateImpl) _then)
: super(_value, _then);
/// Create a copy of ConsultantCommentsState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? commentsModel = null,
Object? dayCommentNeedsToBeFilled = null,
}) {
return _then(_$ConsultantCommentsStateImpl(
commentsModel: null == commentsModel
? _value.commentsModel
: commentsModel // ignore: cast_nullable_to_non_nullable
as ConsultantCommentModel,
dayCommentNeedsToBeFilled: null == dayCommentNeedsToBeFilled
? _value.dayCommentNeedsToBeFilled
: dayCommentNeedsToBeFilled // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
/// @nodoc
class _$ConsultantCommentsStateImpl implements _ConsultantCommentsState {
_$ConsultantCommentsStateImpl(
{required this.commentsModel, this.dayCommentNeedsToBeFilled = false});
@override
final ConsultantCommentModel commentsModel;
@override
@JsonKey()
final bool dayCommentNeedsToBeFilled;
@override
String toString() {
return 'ConsultantCommentsState(commentsModel: $commentsModel, dayCommentNeedsToBeFilled: $dayCommentNeedsToBeFilled)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ConsultantCommentsStateImpl &&
(identical(other.commentsModel, commentsModel) ||
other.commentsModel == commentsModel) &&
(identical(other.dayCommentNeedsToBeFilled,
dayCommentNeedsToBeFilled) ||
other.dayCommentNeedsToBeFilled == dayCommentNeedsToBeFilled));
}
@override
int get hashCode =>
Object.hash(runtimeType, commentsModel, dayCommentNeedsToBeFilled);
/// Create a copy of ConsultantCommentsState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ConsultantCommentsStateImplCopyWith<_$ConsultantCommentsStateImpl>
get copyWith => __$$ConsultantCommentsStateImplCopyWithImpl<
_$ConsultantCommentsStateImpl>(this, _$identity);
}
abstract class _ConsultantCommentsState implements ConsultantCommentsState {
factory _ConsultantCommentsState(
{required final ConsultantCommentModel commentsModel,
final bool dayCommentNeedsToBeFilled}) = _$ConsultantCommentsStateImpl;
@override
ConsultantCommentModel get commentsModel;
@override
bool get dayCommentNeedsToBeFilled;
/// Create a copy of ConsultantCommentsState
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ConsultantCommentsStateImplCopyWith<_$ConsultantCommentsStateImpl>
get copyWith => throw _privateConstructorUsedError;
}

View File

@@ -2,6 +2,9 @@ import 'dart:developer';
import 'dart:io';
import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart';
import 'package:eatvio_app/core/utils/date_time_utils.dart';
import 'package:eatvio_app/features/consultations/models/consultation_model.dart';
import 'package:eatvio_app/features/diary/models/consultant_review_model.dart';
import 'package:eatvio_app/features/diary/models/diary_day_model.dart';
import 'package:eatvio_app/features/diary/models/nutrition_model.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
@@ -16,12 +19,17 @@ class DiaryCubit extends Cubit<DiaryState> {
late int selectedMeal;
final DiaryRepository diaryRepository;
final GlossaryRepository glossaryRepository;
int? diaryId;
DiaryCubit({required this.diaryRepository, required this.glossaryRepository}) : super(const DiaryState.loading()) {
today = DateTime.now();
selectedDay = today;
diaryRepository.forceFetchStream.listen((_) {
fetchDiary(true, diaryId);
});
}
String comment = '';
setSelectedDay(DateTime day) async {
selectedDay = day;
await fetchDiary(true);
@@ -32,21 +40,41 @@ class DiaryCubit extends Cubit<DiaryState> {
emit((state as DiaryStateLoaded).copyWith(selectedMeal: selectedMeal));
}
fetchDiary([bool needLoading = false]) async {
fetchDiary([bool needLoading = false, int? diaryId]) async {
this.diaryId ??= diaryId;
if (needLoading) {
emit(const DiaryState.loading());
selectedMeal = 1;
}
try {
final diaryDay = await diaryRepository.getDiaryByDate('$selectedDay');
final DiaryDayModel diaryDay;
if (diaryId != null) {
diaryDay = await diaryRepository.getDiaryDay(diaryId);
} else {
final stringDate = selectedDay.toUtc().format('yyyy-MM-dd');
diaryDay = await diaryRepository.getDiaryByDate(stringDate);
}
ConsultantReviewModel? consultantReview;
if (diaryDay.chosenPlanId != null) {
consultantReview = await diaryRepository.getDiaryComments(planId: diaryDay.chosenPlanId!, diaryId: diaryDay.id);
}
final nutrition = await getNutrition(diaryDay.id);
final isDiaryFinished = diaryDay.typeOfDiary.diaryState == DiaryStateEnum.finished || diaryDay.typeOfDiary.diaryState == DiaryStateEnum.lastDayFinished;
final isDiaryCommented = diaryDay.typeOfDiary.diaryState == DiaryStateEnum.commented ||
diaryDay.typeOfDiary.diaryState == DiaryStateEnum.lastDayCommented ||
diaryDay.typeOfDiary.diaryState == DiaryStateEnum.reviewedByUser;
emit(
DiaryState.loaded(
diary: diaryDay,
nutrition: nutrition,
selectedDay: selectedDay,
selectedMeal: selectedMeal,
isConsultantView: this.diaryId != null,
consultantReview: consultantReview,
isDiaryFinished: isDiaryFinished,
isDiaryCommented: isDiaryCommented,
),
);
} catch (e, st) {
@@ -55,7 +83,11 @@ class DiaryCubit extends Cubit<DiaryState> {
}
}
finishDiaryDay({required int diaryId, required String wakeUp, required String retire, String? comment}) async {
finishDiaryDay({
required int diaryId,
required String wakeUp,
required String retire,
}) async {
await diaryRepository.finishDiaryDay(diaryId: diaryId, wakeUp: wakeUp, retire: retire, comment: comment);
await fetchDiary();
}
@@ -192,6 +224,6 @@ class DiaryCubit extends Cubit<DiaryState> {
}
fillComment(String comment) {
emit((state as DiaryStateLoaded).copyWith(diary: (state as DiaryStateLoaded).diary.copyWith(comment: comment)));
this.comment = comment;
}
}

View File

@@ -1,3 +1,4 @@
import 'package:eatvio_app/features/diary/models/consultant_review_model.dart';
import 'package:eatvio_app/features/diary/models/diary_day_model.dart';
import 'package:eatvio_app/features/diary/models/nutrition_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -19,6 +20,9 @@ class DiaryState with _$DiaryState {
required NutritionDataModel nutrition,
required DateTime selectedDay,
required int selectedMeal,
@Default(false) bool isConsultantView,
ConsultantReviewModel? consultantReview,
@Default(false) bool isDiaryFinished,
@Default(false) bool isDiaryCommented,
}) = DiaryStateLoaded;
}

View File

@@ -21,8 +21,15 @@ mixin _$DiaryState {
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(String errorMessage) error,
required TResult Function(DiaryDayModel diary, NutritionDataModel nutrition,
DateTime selectedDay, int selectedMeal)
required TResult Function(
DiaryDayModel diary,
NutritionDataModel nutrition,
DateTime selectedDay,
int selectedMeal,
bool isConsultantView,
ConsultantReviewModel? consultantReview,
bool isDiaryFinished,
bool isDiaryCommented)
loaded,
}) =>
throw _privateConstructorUsedError;
@@ -31,8 +38,15 @@ mixin _$DiaryState {
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(String errorMessage)? error,
TResult? Function(DiaryDayModel diary, NutritionDataModel nutrition,
DateTime selectedDay, int selectedMeal)?
TResult? Function(
DiaryDayModel diary,
NutritionDataModel nutrition,
DateTime selectedDay,
int selectedMeal,
bool isConsultantView,
ConsultantReviewModel? consultantReview,
bool isDiaryFinished,
bool isDiaryCommented)?
loaded,
}) =>
throw _privateConstructorUsedError;
@@ -41,8 +55,15 @@ mixin _$DiaryState {
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(String errorMessage)? error,
TResult Function(DiaryDayModel diary, NutritionDataModel nutrition,
DateTime selectedDay, int selectedMeal)?
TResult Function(
DiaryDayModel diary,
NutritionDataModel nutrition,
DateTime selectedDay,
int selectedMeal,
bool isConsultantView,
ConsultantReviewModel? consultantReview,
bool isDiaryFinished,
bool isDiaryCommented)?
loaded,
required TResult orElse(),
}) =>
@@ -139,8 +160,15 @@ class _$DiaryStateImpl implements _DiaryState {
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(String errorMessage) error,
required TResult Function(DiaryDayModel diary, NutritionDataModel nutrition,
DateTime selectedDay, int selectedMeal)
required TResult Function(
DiaryDayModel diary,
NutritionDataModel nutrition,
DateTime selectedDay,
int selectedMeal,
bool isConsultantView,
ConsultantReviewModel? consultantReview,
bool isDiaryFinished,
bool isDiaryCommented)
loaded,
}) {
return $default();
@@ -152,8 +180,15 @@ class _$DiaryStateImpl implements _DiaryState {
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(String errorMessage)? error,
TResult? Function(DiaryDayModel diary, NutritionDataModel nutrition,
DateTime selectedDay, int selectedMeal)?
TResult? Function(
DiaryDayModel diary,
NutritionDataModel nutrition,
DateTime selectedDay,
int selectedMeal,
bool isConsultantView,
ConsultantReviewModel? consultantReview,
bool isDiaryFinished,
bool isDiaryCommented)?
loaded,
}) {
return $default?.call();
@@ -165,8 +200,15 @@ class _$DiaryStateImpl implements _DiaryState {
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(String errorMessage)? error,
TResult Function(DiaryDayModel diary, NutritionDataModel nutrition,
DateTime selectedDay, int selectedMeal)?
TResult Function(
DiaryDayModel diary,
NutritionDataModel nutrition,
DateTime selectedDay,
int selectedMeal,
bool isConsultantView,
ConsultantReviewModel? consultantReview,
bool isDiaryFinished,
bool isDiaryCommented)?
loaded,
required TResult orElse(),
}) {
@@ -262,8 +304,15 @@ class _$DiaryStateLoadingImpl implements DiaryStateLoading {
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(String errorMessage) error,
required TResult Function(DiaryDayModel diary, NutritionDataModel nutrition,
DateTime selectedDay, int selectedMeal)
required TResult Function(
DiaryDayModel diary,
NutritionDataModel nutrition,
DateTime selectedDay,
int selectedMeal,
bool isConsultantView,
ConsultantReviewModel? consultantReview,
bool isDiaryFinished,
bool isDiaryCommented)
loaded,
}) {
return loading();
@@ -275,8 +324,15 @@ class _$DiaryStateLoadingImpl implements DiaryStateLoading {
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(String errorMessage)? error,
TResult? Function(DiaryDayModel diary, NutritionDataModel nutrition,
DateTime selectedDay, int selectedMeal)?
TResult? Function(
DiaryDayModel diary,
NutritionDataModel nutrition,
DateTime selectedDay,
int selectedMeal,
bool isConsultantView,
ConsultantReviewModel? consultantReview,
bool isDiaryFinished,
bool isDiaryCommented)?
loaded,
}) {
return loading?.call();
@@ -288,8 +344,15 @@ class _$DiaryStateLoadingImpl implements DiaryStateLoading {
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(String errorMessage)? error,
TResult Function(DiaryDayModel diary, NutritionDataModel nutrition,
DateTime selectedDay, int selectedMeal)?
TResult Function(
DiaryDayModel diary,
NutritionDataModel nutrition,
DateTime selectedDay,
int selectedMeal,
bool isConsultantView,
ConsultantReviewModel? consultantReview,
bool isDiaryFinished,
bool isDiaryCommented)?
loaded,
required TResult orElse(),
}) {
@@ -414,8 +477,15 @@ class _$DiaryStateErrorImpl implements DiaryStateError {
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(String errorMessage) error,
required TResult Function(DiaryDayModel diary, NutritionDataModel nutrition,
DateTime selectedDay, int selectedMeal)
required TResult Function(
DiaryDayModel diary,
NutritionDataModel nutrition,
DateTime selectedDay,
int selectedMeal,
bool isConsultantView,
ConsultantReviewModel? consultantReview,
bool isDiaryFinished,
bool isDiaryCommented)
loaded,
}) {
return error(errorMessage);
@@ -427,8 +497,15 @@ class _$DiaryStateErrorImpl implements DiaryStateError {
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(String errorMessage)? error,
TResult? Function(DiaryDayModel diary, NutritionDataModel nutrition,
DateTime selectedDay, int selectedMeal)?
TResult? Function(
DiaryDayModel diary,
NutritionDataModel nutrition,
DateTime selectedDay,
int selectedMeal,
bool isConsultantView,
ConsultantReviewModel? consultantReview,
bool isDiaryFinished,
bool isDiaryCommented)?
loaded,
}) {
return error?.call(errorMessage);
@@ -440,8 +517,15 @@ class _$DiaryStateErrorImpl implements DiaryStateError {
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(String errorMessage)? error,
TResult Function(DiaryDayModel diary, NutritionDataModel nutrition,
DateTime selectedDay, int selectedMeal)?
TResult Function(
DiaryDayModel diary,
NutritionDataModel nutrition,
DateTime selectedDay,
int selectedMeal,
bool isConsultantView,
ConsultantReviewModel? consultantReview,
bool isDiaryFinished,
bool isDiaryCommented)?
loaded,
required TResult orElse(),
}) {
@@ -512,10 +596,15 @@ abstract class _$$DiaryStateLoadedImplCopyWith<$Res> {
{DiaryDayModel diary,
NutritionDataModel nutrition,
DateTime selectedDay,
int selectedMeal});
int selectedMeal,
bool isConsultantView,
ConsultantReviewModel? consultantReview,
bool isDiaryFinished,
bool isDiaryCommented});
$DiaryDayModelCopyWith<$Res> get diary;
$NutritionDataModelCopyWith<$Res> get nutrition;
$ConsultantReviewModelCopyWith<$Res>? get consultantReview;
}
/// @nodoc
@@ -535,6 +624,10 @@ class __$$DiaryStateLoadedImplCopyWithImpl<$Res>
Object? nutrition = null,
Object? selectedDay = null,
Object? selectedMeal = null,
Object? isConsultantView = null,
Object? consultantReview = freezed,
Object? isDiaryFinished = null,
Object? isDiaryCommented = null,
}) {
return _then(_$DiaryStateLoadedImpl(
diary: null == diary
@@ -553,6 +646,22 @@ class __$$DiaryStateLoadedImplCopyWithImpl<$Res>
? _value.selectedMeal
: selectedMeal // ignore: cast_nullable_to_non_nullable
as int,
isConsultantView: null == isConsultantView
? _value.isConsultantView
: isConsultantView // ignore: cast_nullable_to_non_nullable
as bool,
consultantReview: freezed == consultantReview
? _value.consultantReview
: consultantReview // ignore: cast_nullable_to_non_nullable
as ConsultantReviewModel?,
isDiaryFinished: null == isDiaryFinished
? _value.isDiaryFinished
: isDiaryFinished // ignore: cast_nullable_to_non_nullable
as bool,
isDiaryCommented: null == isDiaryCommented
? _value.isDiaryCommented
: isDiaryCommented // ignore: cast_nullable_to_non_nullable
as bool,
));
}
@@ -575,6 +684,21 @@ class __$$DiaryStateLoadedImplCopyWithImpl<$Res>
return _then(_value.copyWith(nutrition: value));
});
}
/// Create a copy of DiaryState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ConsultantReviewModelCopyWith<$Res>? get consultantReview {
if (_value.consultantReview == null) {
return null;
}
return $ConsultantReviewModelCopyWith<$Res>(_value.consultantReview!,
(value) {
return _then(_value.copyWith(consultantReview: value));
});
}
}
/// @nodoc
@@ -584,7 +708,11 @@ class _$DiaryStateLoadedImpl implements DiaryStateLoaded {
{required this.diary,
required this.nutrition,
required this.selectedDay,
required this.selectedMeal});
required this.selectedMeal,
this.isConsultantView = false,
this.consultantReview,
this.isDiaryFinished = false,
this.isDiaryCommented = false});
@override
final DiaryDayModel diary;
@@ -594,10 +722,21 @@ class _$DiaryStateLoadedImpl implements DiaryStateLoaded {
final DateTime selectedDay;
@override
final int selectedMeal;
@override
@JsonKey()
final bool isConsultantView;
@override
final ConsultantReviewModel? consultantReview;
@override
@JsonKey()
final bool isDiaryFinished;
@override
@JsonKey()
final bool isDiaryCommented;
@override
String toString() {
return 'DiaryState.loaded(diary: $diary, nutrition: $nutrition, selectedDay: $selectedDay, selectedMeal: $selectedMeal)';
return 'DiaryState.loaded(diary: $diary, nutrition: $nutrition, selectedDay: $selectedDay, selectedMeal: $selectedMeal, isConsultantView: $isConsultantView, consultantReview: $consultantReview, isDiaryFinished: $isDiaryFinished, isDiaryCommented: $isDiaryCommented)';
}
@override
@@ -611,12 +750,28 @@ class _$DiaryStateLoadedImpl implements DiaryStateLoaded {
(identical(other.selectedDay, selectedDay) ||
other.selectedDay == selectedDay) &&
(identical(other.selectedMeal, selectedMeal) ||
other.selectedMeal == selectedMeal));
other.selectedMeal == selectedMeal) &&
(identical(other.isConsultantView, isConsultantView) ||
other.isConsultantView == isConsultantView) &&
(identical(other.consultantReview, consultantReview) ||
other.consultantReview == consultantReview) &&
(identical(other.isDiaryFinished, isDiaryFinished) ||
other.isDiaryFinished == isDiaryFinished) &&
(identical(other.isDiaryCommented, isDiaryCommented) ||
other.isDiaryCommented == isDiaryCommented));
}
@override
int get hashCode =>
Object.hash(runtimeType, diary, nutrition, selectedDay, selectedMeal);
int get hashCode => Object.hash(
runtimeType,
diary,
nutrition,
selectedDay,
selectedMeal,
isConsultantView,
consultantReview,
isDiaryFinished,
isDiaryCommented);
/// Create a copy of DiaryState
/// with the given fields replaced by the non-null parameter values.
@@ -633,11 +788,19 @@ class _$DiaryStateLoadedImpl implements DiaryStateLoaded {
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(String errorMessage) error,
required TResult Function(DiaryDayModel diary, NutritionDataModel nutrition,
DateTime selectedDay, int selectedMeal)
required TResult Function(
DiaryDayModel diary,
NutritionDataModel nutrition,
DateTime selectedDay,
int selectedMeal,
bool isConsultantView,
ConsultantReviewModel? consultantReview,
bool isDiaryFinished,
bool isDiaryCommented)
loaded,
}) {
return loaded(diary, nutrition, selectedDay, selectedMeal);
return loaded(diary, nutrition, selectedDay, selectedMeal, isConsultantView,
consultantReview, isDiaryFinished, isDiaryCommented);
}
@override
@@ -646,11 +809,19 @@ class _$DiaryStateLoadedImpl implements DiaryStateLoaded {
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(String errorMessage)? error,
TResult? Function(DiaryDayModel diary, NutritionDataModel nutrition,
DateTime selectedDay, int selectedMeal)?
TResult? Function(
DiaryDayModel diary,
NutritionDataModel nutrition,
DateTime selectedDay,
int selectedMeal,
bool isConsultantView,
ConsultantReviewModel? consultantReview,
bool isDiaryFinished,
bool isDiaryCommented)?
loaded,
}) {
return loaded?.call(diary, nutrition, selectedDay, selectedMeal);
return loaded?.call(diary, nutrition, selectedDay, selectedMeal,
isConsultantView, consultantReview, isDiaryFinished, isDiaryCommented);
}
@override
@@ -659,13 +830,28 @@ class _$DiaryStateLoadedImpl implements DiaryStateLoaded {
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(String errorMessage)? error,
TResult Function(DiaryDayModel diary, NutritionDataModel nutrition,
DateTime selectedDay, int selectedMeal)?
TResult Function(
DiaryDayModel diary,
NutritionDataModel nutrition,
DateTime selectedDay,
int selectedMeal,
bool isConsultantView,
ConsultantReviewModel? consultantReview,
bool isDiaryFinished,
bool isDiaryCommented)?
loaded,
required TResult orElse(),
}) {
if (loaded != null) {
return loaded(diary, nutrition, selectedDay, selectedMeal);
return loaded(
diary,
nutrition,
selectedDay,
selectedMeal,
isConsultantView,
consultantReview,
isDiaryFinished,
isDiaryCommented);
}
return orElse();
}
@@ -713,12 +899,20 @@ abstract class DiaryStateLoaded implements DiaryState {
{required final DiaryDayModel diary,
required final NutritionDataModel nutrition,
required final DateTime selectedDay,
required final int selectedMeal}) = _$DiaryStateLoadedImpl;
required final int selectedMeal,
final bool isConsultantView,
final ConsultantReviewModel? consultantReview,
final bool isDiaryFinished,
final bool isDiaryCommented}) = _$DiaryStateLoadedImpl;
DiaryDayModel get diary;
NutritionDataModel get nutrition;
DateTime get selectedDay;
int get selectedMeal;
bool get isConsultantView;
ConsultantReviewModel? get consultantReview;
bool get isDiaryFinished;
bool get isDiaryCommented;
/// Create a copy of DiaryState
/// with the given fields replaced by the non-null parameter values.

View File

@@ -1,31 +1,62 @@
import 'dart:developer';
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/data_error_widget.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_cubit.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/calendar_widget.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/calories_chart_widget.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/comment_container.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/comment_for_consultant_widget.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/day_comment_widget.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/finish_day_button_widget.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/meal_widget.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/wakeup_and_retire_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class DiaryScreen extends StatefulWidget {
class DiaryScreen extends StatelessWidget {
const DiaryScreen({super.key});
@override
State<DiaryScreen> createState() => _DiaryScreenState();
Widget build(BuildContext context) {
return const DiaryScreenContent();
}
}
class _DiaryScreenState extends State<DiaryScreen> {
class UserDiaryScreen extends StatelessWidget {
static const path = '/user-diary-screen';
static const name = 'userDiaryScreen';
const UserDiaryScreen({super.key, required this.diaryId});
final int diaryId;
@override
Widget build(BuildContext context) {
return BlocProvider<DiaryCubit>(
create: (BuildContext context) => sl<DiaryCubit>()..fetchDiary(true),
return DiaryScreenContent(diaryId: diaryId);
}
}
class DiaryScreenContent extends StatelessWidget {
const DiaryScreenContent({super.key, this.diaryId});
final int? diaryId;
@override
Widget build(BuildContext context) {
final s = S.of(context);
return MultiBlocProvider(
providers: [
BlocProvider<DiaryCubit>(
create: (BuildContext context) => sl<DiaryCubit>()..fetchDiary(true, diaryId),
),
BlocProvider(
create: (context) => sl<ConsultantCommentsCubit>(),
),
],
child: BlocBuilder<DiaryCubit, DiaryState>(
builder: (BuildContext context, DiaryState state) {
if (state is DiaryStateLoaded) {
@@ -33,9 +64,10 @@ class _DiaryScreenState extends State<DiaryScreen> {
}
if (state is DiaryStateError) {
return Scaffold(
body: DataErrorWidget(onReload: () => context.read<DiaryCubit>().fetchDiary()),
body: DataErrorWidget(onReload: () => context.read<DiaryCubit>().fetchDiary(false, diaryId)),
);
}
return Scaffold(
backgroundColor: Colors.grey.shade200,
appBar: AppBar(
@@ -46,24 +78,79 @@ class _DiaryScreenState extends State<DiaryScreen> {
physics: const BouncingScrollPhysics(),
child: Column(
children: [
const CalendarWidget(),
if (diaryId == null) const CalendarWidget(),
if (state is DiaryStateLoading) const Loader(),
if (state is DiaryStateLoaded) WakeupAndRetireWidget(showRetire: state.diary.typeOfDiary.hasChosenPlan),
if (state is DiaryStateLoaded)
WakeupAndRetireWidget(
showRetire: state.diary.typeOfDiary.hasChosenPlan,
disable: state.isConsultantView || state.isDiaryFinished || state.isDiaryCommented,
),
const SizedBox(height: 12),
if (state is DiaryStateLoaded)
MealWidget(
showMood: state.diary.typeOfDiary.hasChosenPlan,
showAddButton: !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented,
),
if (state is DiaryStateLoaded) const CaloriesChartWidget(),
const SizedBox(height: 12),
if (state is DiaryStateLoaded && state.diary.typeOfDiary.hasChosenPlan) const CommentForConsultantWidget(),
if (state is DiaryStateLoaded && state.diary.closed == 0)
Builder(builder: (context) {
if (state is DiaryStateLoaded && state.diary.typeOfDiary.hasChosenPlan) {
if (state.diary.comment.isNotEmpty) {
return CommentForConsultantWidget(
comment: state.diary.comment,
);
}
if (state.isDiaryCommented || state.isDiaryFinished) {
return const SizedBox.shrink();
}
return const CommentForConsultantWidget();
}
return const SizedBox.shrink();
}),
if (state is DiaryStateLoaded && !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented)
Padding(
padding: const EdgeInsets.only(top: 12),
child: FinishDayButtonWidget(
diaryDay: state.diary,
),
),
if (state is DiaryStateLoaded && state.isConsultantView && !state.isDiaryCommented) ...[
const SizedBox(height: 16),
const MainBackgroundBouble(
padding: EdgeInsets.all(16),
child: DayCommentWidget(),
)
],
if (state is DiaryStateLoaded && state.consultantReview?.consultantComments.dayComment != null) ...[
const SizedBox(height: 12),
MainBackgroundBouble(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Text(
s.dayComment,
style: TextStyles.textLSemibold.copyWith(color: appTheme.black),
),
const SizedBox(height: 12),
CommentContainer(comment: state.consultantReview?.consultantComments.dayComment ?? ''),
],
),
),
const SizedBox(height: 28),
],
if (state is DiaryStateLoaded && state.isConsultantView && state.isDiaryFinished && !state.isDiaryCommented) ...[
const SizedBox(height: 16),
CustomOutlinedButton(
text: s.send,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.blueA400)),
onPressed: () async {
await context.read<ConsultantCommentsCubit>().submitComments(state.diary.id);
},
),
const SizedBox(height: 16),
],
],
),
),

View File

@@ -0,0 +1,23 @@
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:flutter/material.dart';
class CommentContainer extends StatelessWidget {
const CommentContainer({super.key, required this.comment});
final String comment;
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
width: double.infinity,
decoration: BoxDecoration(
color: appTheme.commentBg,
borderRadius: BorderRadius.circular(20),
),
child: Text(
comment,
style: TextStyles.textMMediumAuto.copyWith(color: appTheme.black),
),
);
}
}

View File

@@ -1,17 +1,26 @@
import 'package:easy_debounce/easy_debounce.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class CommentForConsultantWidget extends StatefulWidget {
const CommentForConsultantWidget({super.key});
const CommentForConsultantWidget({super.key, this.comment});
final String? comment;
@override
State<CommentForConsultantWidget> createState() => _CommentForConsultantWidgetState();
}
class _CommentForConsultantWidgetState extends State<CommentForConsultantWidget> {
final controller = TextEditingController();
@override
void initState() {
super.initState();
if (widget.comment != null) controller.text = widget.comment ?? '';
}
@override
Widget build(BuildContext context) {
return Material(
@@ -22,19 +31,22 @@ class _CommentForConsultantWidgetState extends State<CommentForConsultantWidget>
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
Text(
'Дополнительная информация для консультанта',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Color(0xFF323F4B), height: 1),
style: TextStyles.textLSemibold.copyWith(color: appTheme.black),
),
const SizedBox(height: 12),
CustomTextFormField(
controller: controller,
onChanged: (value) {
EasyDebounce.debounce('client-typing', const Duration(seconds: 3), () => context.read<DiaryCubit>().fillComment(value));
EasyDebounce.debounce('client-typing', const Duration(milliseconds: 500), () => context.read<DiaryCubit>().fillComment(value));
},
hintText: 'Укажите дополнительную информацию',
minLines: 4,
maxLines: 4,
readOnly: widget.comment != null,
),
],
),

View File

@@ -0,0 +1,45 @@
import 'package:easy_debounce/easy_debounce.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_cubit.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_state.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class DayCommentWidget extends StatelessWidget {
const DayCommentWidget({super.key});
@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocBuilder<ConsultantCommentsCubit, ConsultantCommentsState>(
builder: (context, state) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
s.dayCommentTitle,
textAlign: TextAlign.center,
style: TextStyles.textLSemibold.copyWith(color: appTheme.black),
),
const SizedBox(height: 12),
CustomTextFormField(
onChanged: (value) {
EasyDebounce.debounce('client-typing', const Duration(milliseconds: 500), () => context.read<ConsultantCommentsCubit>().setComment(value));
},
hasError: state.dayCommentNeedsToBeFilled,
hintText: s.typeDayComment,
minLines: 4,
maxLines: 4,
readOnly: false,
),
if (state.dayCommentNeedsToBeFilled) ...[const SizedBox(height: 4), const ValidationErrorWidget(error: 'Минимум 3 символа')],
],
);
},
);
}
}

View File

@@ -17,12 +17,10 @@ class _FinishDayButtonWidgetState extends State<FinishDayButtonWidget> {
@override
Widget build(BuildContext context) {
return InkWell(
onTap:
() => context.read<DiaryCubit>().finishDiaryDay(
onTap: () => context.read<DiaryCubit>().finishDiaryDay(
diaryId: widget.diaryDay.id,
wakeUp: widget.diaryDay.wakeup.toString(),
retire: widget.diaryDay.retire.toString(),
comment: widget.diaryDay.comment.isEmpty ? null : widget.diaryDay.comment,
),
borderRadius: BorderRadius.circular(20),
child: Ink(

View File

@@ -0,0 +1,74 @@
import 'package:easy_debounce/easy_debounce.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_cubit.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_state.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class MealCommentWidget extends StatefulWidget {
const MealCommentWidget({super.key, required this.selectedMeal});
final int selectedMeal;
@override
State<MealCommentWidget> createState() => _MealCommentWidgetState();
}
class _MealCommentWidgetState extends State<MealCommentWidget> {
final controller = TextEditingController();
@override
void didUpdateWidget(MealCommentWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.selectedMeal != oldWidget.selectedMeal) {
final state = context.read<ConsultantCommentsCubit>().state;
controller.text = state.commentsModel.mealsComments[widget.selectedMeal.toString()]?.comment ?? '';
}
}
@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocBuilder<ConsultantCommentsCubit, ConsultantCommentsState>(
builder: (context, state) {
final bool hasError;
if (state.commentsModel.mealsComments[widget.selectedMeal.toString()] != null &&
state.commentsModel.mealsComments[widget.selectedMeal.toString()]!.comment.length < 3) {
hasError = true;
} else {
hasError = false;
}
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'${s.commentForMeal} ${widget.selectedMeal}',
textAlign: TextAlign.center,
style: TextStyles.textLSemibold.copyWith(color: appTheme.black),
),
const SizedBox(height: 12),
CustomTextFormField(
controller: controller,
hasError: hasError,
onChanged: (value) {
EasyDebounce.debounce(
'client-typing',
const Duration(milliseconds: 500),
() => context.read<ConsultantCommentsCubit>().setMealComment(mealNumber: widget.selectedMeal, comment: value),
);
},
hintText: s.typeMealComment,
minLines: 4,
maxLines: 4,
readOnly: false,
),
if (hasError) ...[const SizedBox(height: 4), const ValidationErrorWidget(error: 'Минимум 3 символа')],
],
);
},
);
}
}

View File

@@ -1,15 +1,24 @@
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/add_product_button_widget.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/comment_container.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/meal_button_widget.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/meal_comment_widget.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/mood_rating_widget.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/photo_gallery_widget.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/products_list_widget.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/satiety_rating_widget.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/time_of_meal_picker_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class MealWidget extends StatefulWidget {
const MealWidget({super.key, required this.showMood});
const MealWidget({super.key, required this.showMood, required this.showAddButton});
final bool showMood;
final bool showAddButton;
@override
State<MealWidget> createState() => _MealWidgetState();
}
@@ -17,6 +26,7 @@ class MealWidget extends StatefulWidget {
class _MealWidgetState extends State<MealWidget> {
@override
Widget build(BuildContext context) {
final s = S.of(context);
return Material(
borderRadius: BorderRadius.circular(20),
color: Colors.white,
@@ -48,8 +58,10 @@ class _MealWidgetState extends State<MealWidget> {
const TimeOfMealPickerWidget(),
const SizedBox(height: 12),
const ProductsListWidget(),
const SizedBox(height: 12),
const AddProductButtonWidget(),
if (widget.showAddButton) ...[
const SizedBox(height: 12),
const AddProductButtonWidget(),
],
const SizedBox(height: 12),
const PhotoGalleryWidget(),
const SizedBox(height: 12),
@@ -57,7 +69,35 @@ class _MealWidgetState extends State<MealWidget> {
if (widget.showMood) ...[
const SizedBox(height: 12),
const MoodRatingWidget(),
]
],
BlocBuilder<DiaryCubit, DiaryState>(
builder: (context, state) {
if (state is DiaryStateLoaded && state.isConsultantView && !state.isDiaryCommented) {
return MealCommentWidget(selectedMeal: state.selectedMeal);
}
return const SizedBox.shrink();
},
),
BlocBuilder<DiaryCubit, DiaryState>(
builder: (context, state) {
if (state is DiaryStateLoaded) {
final mealsComments = state.consultantReview?.consultantComments.mealsComments;
if (mealsComments != null && (state.selectedMeal - 1 < mealsComments.length)) {
return Column(
children: [
Text(
'${s.mealComment} ${state.selectedMeal}',
style: TextStyles.textLSemibold.copyWith(color: appTheme.black),
),
const SizedBox(height: 12),
CommentContainer(comment: mealsComments[state.selectedMeal - 1].comment),
],
);
}
}
return const SizedBox.shrink();
},
)
],
),
),

View File

@@ -32,9 +32,10 @@ class _MoodRatingWidgetState extends State<MoodRatingWidget> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'Ваше настроение после приёма пищи',
style: TextStyle(
Text(
state.isConsultantView ? 'Настроение пользователя после приема пищи' : 'Ваше настроение после приёма пищи',
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 16,
),
),
@@ -47,6 +48,7 @@ class _MoodRatingWidgetState extends State<MoodRatingWidget> {
width: 32,
),
RatingBar.builder(
ignoreGestures: state.isConsultantView || state.isDiaryFinished || state.isDiaryCommented,
initialRating: state.diary.meals[selectedIndex].mood.toDouble() + 1,
itemCount: 6,
itemSize: MediaQuery.of(context).size.width / 6 - (124 / 6),

View File

@@ -90,7 +90,7 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
builder: (context, state) {
if (state is DiaryStateLoaded) {
int mealIndex = state.selectedMeal - 1;
if (state.diary.meals[mealIndex].photos.isEmpty) {
if (state.diary.meals[mealIndex].photos.isEmpty && !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented) {
return GestureDetector(
onTap: () => showImagePicker(state.diary),
child: SvgPicture.asset(
@@ -99,6 +99,9 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
),
);
} else {
if (state.diary.meals[mealIndex].photos.isEmpty) {
return const SizedBox.shrink();
}
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
@@ -112,30 +115,31 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
imageUrl: '$baseUrl/storage/${state.diary.meals[mealIndex].photos[currentSelectedPhotoIndex].path}',
),
),
Padding(
padding: const EdgeInsets.all(12),
child: IconButton.filled(
style: IconButton.styleFrom(
backgroundColor: Colors.white,
elevation: 0,
),
onPressed: () async {
await diaryCubit.deletePhoto(
photo: state.diary.meals[mealIndex].photos[currentSelectedPhotoIndex],
diaryId: state.diary.id,
);
setState(() {
if (currentSelectedPhotoIndex == state.diary.meals[mealIndex].photos.length - 1 && currentSelectedPhotoIndex != 0) {
currentSelectedPhotoIndex = state.diary.meals[mealIndex].photos.length - 2;
}
});
},
icon: const Icon(
Icons.close_rounded,
color: Colors.grey,
if (!state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented)
Padding(
padding: const EdgeInsets.all(12),
child: IconButton.filled(
style: IconButton.styleFrom(
backgroundColor: Colors.white,
elevation: 0,
),
onPressed: () async {
await diaryCubit.deletePhoto(
photo: state.diary.meals[mealIndex].photos[currentSelectedPhotoIndex],
diaryId: state.diary.id,
);
setState(() {
if (currentSelectedPhotoIndex == state.diary.meals[mealIndex].photos.length - 1 && currentSelectedPhotoIndex != 0) {
currentSelectedPhotoIndex = state.diary.meals[mealIndex].photos.length - 2;
}
});
},
icon: const Icon(
Icons.close_rounded,
color: Colors.grey,
),
),
),
),
],
),
const SizedBox(height: 8),
@@ -147,7 +151,7 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
mainAxisAlignment: MainAxisAlignment.start,
children: [
for (int i = 0; i < state.diary.meals[mealIndex].photos.length + 1; i++) ...[
if (i == state.diary.meals[mealIndex].photos.length)
if (i == state.diary.meals[mealIndex].photos.length && !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented)
Padding(
padding: const EdgeInsets.only(left: 8),
child: InkWell(

View File

@@ -0,0 +1,86 @@
import 'package:collection/collection.dart';
import 'package:easy_debounce/easy_debounce.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
import 'package:eatvio_app/features/diary/models/consultant_comment_model.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_cubit.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_state.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/comment_container.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class PortionCommentWidget extends StatefulWidget {
const PortionCommentWidget({super.key, required this.enabled, required this.portionId, this.comment});
final bool enabled;
final int portionId;
final String? comment;
@override
State<PortionCommentWidget> createState() => _PortionCommentWidgetState();
}
class _PortionCommentWidgetState extends State<PortionCommentWidget> {
final controller = TextEditingController();
@override
void didUpdateWidget(PortionCommentWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.portionId != oldWidget.portionId) {
final state = context.read<ConsultantCommentsCubit>().state;
controller.text = state.commentsModel.portions.firstWhereOrNull((portion) => portion.id == widget.portionId)?.comment ?? '';
}
}
@override
void initState() {
super.initState();
final state = context.read<ConsultantCommentsCubit>().state;
controller.text = state.commentsModel.portions.firstWhereOrNull((portion) => portion.id == widget.portionId)?.comment ?? '';
}
@override
Widget build(BuildContext context) {
final s = S.of(context);
if (widget.enabled) {
return BlocBuilder<ConsultantCommentsCubit, ConsultantCommentsState>(
builder: (context, state) {
final bool hasError;
if (state.commentsModel.portions.firstWhereOrNull((elem) => elem.id == widget.portionId) != null &&
state.commentsModel.portions.firstWhereOrNull((elem) => elem.id == widget.portionId)!.comment.length < 3) {
hasError = true;
} else {
hasError = false;
}
return Column(
children: [
CustomTextFormField(
controller: controller,
hintText: s.typePortionComment,
hasError: hasError,
onChanged: (value) {
EasyDebounce.debounce(
'client-typing',
const Duration(milliseconds: 500),
() {
final hasPortionComment = state.commentsModel.portions.any((portion) => portion.id == widget.portionId);
if (hasPortionComment) {
final portion = state.commentsModel.portions.firstWhere((portion) => portion.id == widget.portionId);
context.read<ConsultantCommentsCubit>().updatePortionComment(portion.copyWith(comment: value));
return;
}
context.read<ConsultantCommentsCubit>().addPortionComment(PortionCommentModel(id: widget.portionId, comment: value));
},
);
},
),
if (hasError) ...[const SizedBox(height: 4), const ValidationErrorWidget(error: 'Минимум 3 символа')],
],
);
},
);
}
if (widget.comment != null) {
return CommentContainer(comment: widget.comment ?? '');
}
return const SizedBox.shrink();
}
}

View File

@@ -1,9 +1,13 @@
import 'package:collection/collection.dart';
import 'package:dotted_border/dotted_border.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/consultations/models/consultation_model.dart';
import 'package:eatvio_app/features/diary/models/consultant_review_model.dart';
import 'package:eatvio_app/features/diary/models/diary_day_model.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/add_product_modal.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/portion_comment_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
@@ -24,7 +28,7 @@ class _ProductWidgetState extends State<ProductWidget> {
late final DiaryCubit diaryCubit;
late String portionName;
late bool isEaten;
bool showCommentWidget = false;
@override
void initState() {
super.initState();
@@ -55,6 +59,17 @@ class _ProductWidgetState extends State<ProductWidget> {
return BlocBuilder<DiaryCubit, DiaryState>(
builder: (context, state) {
if (state is DiaryStateLoaded) {
final isDiaryCommented = state.diary.typeOfDiary.diaryState == DiaryStateEnum.commented ||
state.diary.typeOfDiary.diaryState == DiaryStateEnum.lastDayCommented ||
state.diary.typeOfDiary.diaryState == DiaryStateEnum.reviewedByUser;
final isDiaryFinished =
state.diary.typeOfDiary.diaryState == DiaryStateEnum.finished || state.diary.typeOfDiary.diaryState == DiaryStateEnum.lastDayFinished;
final canEditPortion = !state.isConsultantView && !isDiaryCommented && !isDiaryFinished;
final consultantComments = state.consultantReview?.consultantComments;
PortionComment? portionComment;
if (consultantComments != null) {
portionComment = consultantComments.portionsComments.firstWhereOrNull((elem) => elem.portionId == widget.portion.id);
}
if (widget.portion.portionState == PortionState.eatenDeleted) {
return DottedBorder(
dashPattern: const [6, 6],
@@ -84,50 +99,74 @@ class _ProductWidgetState extends State<ProductWidget> {
),
);
} else {
return ListTile(
onTap: () => updateProductInMeal(state, widget.portion),
trailing: const Icon(Icons.more_vert_rounded),
leading: widget.portion.fromDiet
? Transform.scale(
scale: 1.2,
child: Checkbox(
value: isEaten,
onChanged: (value) {
setState(() {
isEaten = value!;
});
if (isEaten) {
diaryCubit.markEaten(
diaryId: state.diary.id,
mealId: state.selectedMeal,
portionId: widget.portion.id,
);
} else {
diaryCubit.unmarkEaten(
diaryId: state.diary.id,
mealId: state.selectedMeal,
portionId: widget.portion.id,
);
}
},
activeColor: appTheme.blueA400,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
side: const BorderSide(
color: Color(0xFF809CB9),
),
),
)
: null,
horizontalTitleGap: 0,
contentPadding: widget.portion.fromDiet ? const EdgeInsets.only(right: 24, left: 4) : null,
title: Text(widget.portion.productName),
subtitle: Text('${widget.portion.quantity} ${widget.portion.portion == 'gr' ? 'гр' : 'шт'}'),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
tileColor: const Color(0xFFF6FAFF),
Widget? trailing;
if (state.isConsultantView && !isDiaryCommented) {
trailing = GestureDetector(
onTap: () {
setState(() {
showCommentWidget = !showCommentWidget;
});
},
child: SvgPicture.asset('assets/icons/edit.svg'));
} else if (!isDiaryCommented) {
trailing = const Icon(Icons.more_vert_rounded);
}
return Column(
children: [
ListTile(
onTap: canEditPortion ? () => updateProductInMeal(state, widget.portion) : null,
trailing: trailing,
leading: widget.portion.fromDiet
? Transform.scale(
scale: 1.2,
child: Checkbox(
value: isEaten,
onChanged: (value) {
setState(() {
isEaten = value!;
});
if (isEaten) {
diaryCubit.markEaten(
diaryId: state.diary.id,
mealId: state.selectedMeal,
portionId: widget.portion.id,
);
} else {
diaryCubit.unmarkEaten(
diaryId: state.diary.id,
mealId: state.selectedMeal,
portionId: widget.portion.id,
);
}
},
activeColor: appTheme.blueA400,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
side: const BorderSide(
color: Color(0xFF809CB9),
),
),
)
: null,
horizontalTitleGap: 0,
contentPadding: widget.portion.fromDiet ? const EdgeInsets.only(right: 24, left: 4) : null,
title: Text(widget.portion.productName),
subtitle: Text('${widget.portion.quantity} ${widget.portion.portion == 'gr' ? 'гр' : 'шт'}'),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
tileColor: const Color(0xFFF6FAFF),
),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: PortionCommentWidget(
enabled: showCommentWidget,
portionId: widget.portion.id,
comment: portionComment?.comment,
),
)
],
);
}
}

View File

@@ -15,7 +15,7 @@ class _ProductsListWidgetState extends State<ProductsListWidget> {
@override
Widget build(BuildContext context) {
return BlocBuilder<DiaryCubit, DiaryState>(
builder: (BuildContext context, DiaryState state) {
builder: (BuildContext context, DiaryState state) {
if (state is DiaryStateLoaded) {
int mealIndex = state.selectedMeal - 1;
return Column(

View File

@@ -32,9 +32,10 @@ class _SatietyRatingWidgetState extends State<SatietyRatingWidget> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'Ваша сытость после приёма пищи',
style: TextStyle(
Text(
state.isConsultantView ? 'Сытость пользователя после приема пищи' : 'Ваша сытость после приёма пищи',
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 16,
),
),
@@ -47,6 +48,7 @@ class _SatietyRatingWidgetState extends State<SatietyRatingWidget> {
width: 32,
),
RatingBar.builder(
ignoreGestures: state.isConsultantView || state.isDiaryFinished || state.isDiaryCommented,
initialRating: state.diary.meals[selectedIndex].satiety.toDouble() + 1,
itemCount: 6,
itemSize: MediaQuery.of(context).size.width / 6 - (124 / 6),
@@ -172,6 +174,7 @@ class _SatietyRatingWidgetState extends State<SatietyRatingWidget> {
}
},
onRatingUpdate: (value) {
if (state.isConsultantView) return;
setState(() {
currentSelectedRating[selectedIndex] = value;
});

View File

@@ -69,6 +69,7 @@ class _TimeOfMealPickerWidgetState extends State<TimeOfMealPickerWidget> {
),
),
),
const SizedBox(height: 30)
],
),
),
@@ -97,7 +98,7 @@ class _TimeOfMealPickerWidgetState extends State<TimeOfMealPickerWidget> {
),
),
InkWell(
onTap: () => showTimePicker(state.diary.id),
onTap: state.isConsultantView || state.isDiaryFinished || state.isDiaryCommented ? null : () => showTimePicker(state.diary.id),
borderRadius: BorderRadius.circular(100),
child: Ink(
height: 32,

View File

@@ -8,8 +8,9 @@ import 'package:flutter_svg/flutter_svg.dart';
import '../../../../core/theme/theme_helper.dart';
class WakeupAndRetireWidget extends StatefulWidget {
const WakeupAndRetireWidget({super.key, required this.showRetire});
const WakeupAndRetireWidget({super.key, required this.showRetire, this.disable = false});
final bool showRetire;
final bool disable;
@override
State<WakeupAndRetireWidget> createState() => _WakeupAndRetireWidgetState();
}
@@ -82,6 +83,7 @@ class _WakeupAndRetireWidgetState extends State<WakeupAndRetireWidget> {
),
),
),
const SizedBox(height: 30)
],
),
),
@@ -125,7 +127,7 @@ class _WakeupAndRetireWidgetState extends State<WakeupAndRetireWidget> {
),
const SizedBox(height: 8),
InkWell(
onTap: () => showTimePicker(state.diary.id, true),
onTap: widget.disable ? null : () => showTimePicker(state.diary.id, true),
borderRadius: BorderRadius.circular(100),
child: Ink(
height: 32,
@@ -185,7 +187,7 @@ class _WakeupAndRetireWidgetState extends State<WakeupAndRetireWidget> {
),
const SizedBox(height: 8),
InkWell(
onTap: () => showTimePicker(state.diary.id, false),
onTap: widget.disable ? null : () => showTimePicker(state.diary.id, false),
borderRadius: BorderRadius.circular(100),
child: Ink(
height: 32,

View File

@@ -1,4 +1,7 @@
import 'dart:async';
import 'dart:io';
import 'package:eatvio_app/features/diary/models/consultant_comment_model.dart';
import 'package:eatvio_app/features/diary/models/consultant_review_model.dart';
import 'package:eatvio_app/features/diary/models/diary_day_model.dart';
import 'package:eatvio_app/features/diary/models/finish_diary_day_model.dart';
import 'package:eatvio_app/features/diary/models/nutrition_model.dart';
@@ -8,6 +11,9 @@ import 'package:eatvio_app/features/glossary/models/glossary_model.dart';
class DiaryRepository {
final DiaryService diaryService;
final StreamController<String> _diaryForceFetchController = StreamController<String>();
Stream<String> get forceFetchStream => _diaryForceFetchController.stream;
DiaryRepository({
required this.diaryService,
@@ -25,6 +31,10 @@ class DiaryRepository {
return (await diaryService.getDiaryIndexByDate(date: date));
}
Future<DiaryDayModel> getDiaryDay(int diaryId) async {
return (await diaryService.getDiaryDay(diary: diaryId)).diary;
}
Future<NutritionDataModel> getNutritionData(int index) async {
return await diaryService.getNutrition(diary: index);
}
@@ -162,11 +172,7 @@ class DiaryRepository {
}) async {
return await diaryService.finishDiaryDay(
diary: diaryId,
finishDiaryDayModel: FinishDiaryDayModel(
wakeUp: wakeUp,
retire: retire,
comment: comment
),
finishDiaryDayModel: FinishDiaryDayModel(wakeUp: wakeUp, retire: retire, comment: comment),
);
}
@@ -263,4 +269,24 @@ class DiaryRepository {
rethrow;
}
}
Future<void> commentUserDay({required int diaryId, required ConsultantCommentModel commentModel}) async {
try {
await diaryService.commentUserDay(id: diaryId, body: commentModel);
_diaryForceFetchController.add('force');
} catch (e) {
rethrow;
}
}
Future<ConsultantReviewModel> getDiaryComments({
required int planId,
required int diaryId,
}) async {
try {
return await diaryService.getDiaryComments(planId: planId, diaryId: diaryId);
} catch (e) {
rethrow;
}
}
}

View File

@@ -1,5 +1,7 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:eatvio_app/features/diary/models/consultant_comment_model.dart';
import 'package:eatvio_app/features/diary/models/consultant_review_model.dart';
import 'package:eatvio_app/features/diary/models/diary_day_model.dart';
import 'package:eatvio_app/features/diary/models/finish_diary_day_model.dart';
import 'package:eatvio_app/features/diary/models/nutrition_model.dart';
@@ -131,4 +133,16 @@ abstract class DiaryService {
Future<GlossaryModerationModel> sendProductToModeration({
@Body() required Map<String, dynamic> data,
});
@POST('/api/diary/{id}/consultant/comment')
Future<void> commentUserDay({
@Path('id') required int id,
@Body() required ConsultantCommentModel body,
});
@GET('/api/chosen_plans/{planId}/{diaryId}/comments/show')
Future<ConsultantReviewModel> getDiaryComments({
@Path() required int planId,
@Path() required int diaryId,
});
}

View File

@@ -478,6 +478,58 @@ class _DiaryService implements DiaryService {
return _value;
}
@override
Future<void> commentUserDay({
required int id,
required ConsultantCommentModel body,
}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
final _data = body;
final _options = _setStreamType<void>(
Options(method: 'POST', headers: _headers, extra: _extra)
.compose(
_dio.options,
'/api/diary/${id}/consultant/comment',
queryParameters: queryParameters,
data: _data,
)
.copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
);
await _dio.fetch<void>(_options);
}
@override
Future<ConsultantReviewModel> getDiaryComments({
required int planId,
required int diaryId,
}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options = _setStreamType<ConsultantReviewModel>(
Options(method: 'GET', headers: _headers, extra: _extra)
.compose(
_dio.options,
'/api/chosen_plans/${planId}/${diaryId}/comments/show',
queryParameters: queryParameters,
data: _data,
)
.copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
);
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late ConsultantReviewModel _value;
try {
_value = ConsultantReviewModel.fromJson(_result.data!);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
return _value;
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||

View File

@@ -0,0 +1,25 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'notification_model.freezed.dart';
part 'notification_model.g.dart';
@freezed
class NotificationModel with _$NotificationModel {
factory NotificationModel({
required NotificationType type
}) = _NotificationModel;
factory NotificationModel.fromJson(Map<String, dynamic> json) => _$NotificationModelFromJson(json);
}
enum NotificationType {
@JsonValue('App\\Notifications\\UserSubscribedNotification')
userSubscribedNotification,
@JsonValue('App\\Notifications\\Plan\\PlanSubscriptionStartedToAuthor')
planSubscriptionStartedToAuthor,
@JsonValue('App\\Notifications\\Plan\\PlanSubscriptionCanceledToAuthor')
planSubscriptionCanceledToAuthor,
}

View File

@@ -0,0 +1,166 @@
// 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 'notification_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');
NotificationModel _$NotificationModelFromJson(Map<String, dynamic> json) {
return _NotificationModel.fromJson(json);
}
/// @nodoc
mixin _$NotificationModel {
NotificationType get type => throw _privateConstructorUsedError;
/// Serializes this NotificationModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of NotificationModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$NotificationModelCopyWith<NotificationModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $NotificationModelCopyWith<$Res> {
factory $NotificationModelCopyWith(
NotificationModel value, $Res Function(NotificationModel) then) =
_$NotificationModelCopyWithImpl<$Res, NotificationModel>;
@useResult
$Res call({NotificationType type});
}
/// @nodoc
class _$NotificationModelCopyWithImpl<$Res, $Val extends NotificationModel>
implements $NotificationModelCopyWith<$Res> {
_$NotificationModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of NotificationModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? type = null,
}) {
return _then(_value.copyWith(
type: null == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
as NotificationType,
) as $Val);
}
}
/// @nodoc
abstract class _$$NotificationModelImplCopyWith<$Res>
implements $NotificationModelCopyWith<$Res> {
factory _$$NotificationModelImplCopyWith(_$NotificationModelImpl value,
$Res Function(_$NotificationModelImpl) then) =
__$$NotificationModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({NotificationType type});
}
/// @nodoc
class __$$NotificationModelImplCopyWithImpl<$Res>
extends _$NotificationModelCopyWithImpl<$Res, _$NotificationModelImpl>
implements _$$NotificationModelImplCopyWith<$Res> {
__$$NotificationModelImplCopyWithImpl(_$NotificationModelImpl _value,
$Res Function(_$NotificationModelImpl) _then)
: super(_value, _then);
/// Create a copy of NotificationModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? type = null,
}) {
return _then(_$NotificationModelImpl(
type: null == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
as NotificationType,
));
}
}
/// @nodoc
@JsonSerializable()
class _$NotificationModelImpl implements _NotificationModel {
_$NotificationModelImpl({required this.type});
factory _$NotificationModelImpl.fromJson(Map<String, dynamic> json) =>
_$$NotificationModelImplFromJson(json);
@override
final NotificationType type;
@override
String toString() {
return 'NotificationModel(type: $type)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$NotificationModelImpl &&
(identical(other.type, type) || other.type == type));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, type);
/// Create a copy of NotificationModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$NotificationModelImplCopyWith<_$NotificationModelImpl> get copyWith =>
__$$NotificationModelImplCopyWithImpl<_$NotificationModelImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$NotificationModelImplToJson(
this,
);
}
}
abstract class _NotificationModel implements NotificationModel {
factory _NotificationModel({required final NotificationType type}) =
_$NotificationModelImpl;
factory _NotificationModel.fromJson(Map<String, dynamic> json) =
_$NotificationModelImpl.fromJson;
@override
NotificationType get type;
/// Create a copy of NotificationModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$NotificationModelImplCopyWith<_$NotificationModelImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -0,0 +1,28 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'notification_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$NotificationModelImpl _$$NotificationModelImplFromJson(
Map<String, dynamic> json) =>
_$NotificationModelImpl(
type: $enumDecode(_$NotificationTypeEnumMap, json['type']),
);
Map<String, dynamic> _$$NotificationModelImplToJson(
_$NotificationModelImpl instance) =>
<String, dynamic>{
'type': _$NotificationTypeEnumMap[instance.type]!,
};
const _$NotificationTypeEnumMap = {
NotificationType.userSubscribedNotification:
'App\\Notifications\\UserSubscribedNotification',
NotificationType.planSubscriptionStartedToAuthor:
'App\\Notifications\\Plan\\PlanSubscriptionStartedToAuthor',
NotificationType.planSubscriptionCanceledToAuthor:
'App\\Notifications\\Plan\\PlanSubscriptionCanceledToAuthor',
};

View File

@@ -0,0 +1,27 @@
import 'package:eatvio_app/core/websockets/ws_manager.dart';
import 'package:eatvio_app/features/notifications/models/notification_model.dart';
import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_state.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class NotificationsCubit extends Cubit<NotificationsState> {
final List<NotificationModel> _notifications = [];
final WsManager wsManager;
NotificationsCubit({required this.wsManager}) : super(const NotificationsState.empty()) {
wsManager.newNotificationsStream?.listen((event) {
_handleNotification(event.data);
});
}
void _handleNotification(String notificationData) {
// final data = jsonDecode(notificationData) as Map<String, dynamic>;
// final notification = NotificationModel.fromJson(data);
// _notifications.add(notification);
emit(
NotificationsState.loaded(
notifications: _notifications,
notificationsCount: _notifications.length,
),
);
}
}

View File

@@ -1,3 +1,4 @@
import 'package:eatvio_app/features/notifications/models/notification_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'notifications_state.freezed.dart';
@@ -9,7 +10,7 @@ abstract class NotificationsState with _$NotificationsState {
const factory NotificationsState.empty() = NotificationsStateEmpty;
const factory NotificationsState.loaded({
required List<String> notifications,
required List<NotificationModel> notifications,
required int notificationsCount,
}) = NotificationsStateLoaded;

View File

@@ -21,7 +21,7 @@ mixin _$NotificationsState {
TResult Function() $default, {
required TResult Function() empty,
required TResult Function(
List<String> notifications, int notificationsCount)
List<NotificationModel> notifications, int notificationsCount)
loaded,
required TResult Function(String errorMessage) error,
}) =>
@@ -30,7 +30,8 @@ mixin _$NotificationsState {
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? empty,
TResult? Function(List<String> notifications, int notificationsCount)?
TResult? Function(
List<NotificationModel> notifications, int notificationsCount)?
loaded,
TResult? Function(String errorMessage)? error,
}) =>
@@ -39,7 +40,8 @@ mixin _$NotificationsState {
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? empty,
TResult Function(List<String> notifications, int notificationsCount)?
TResult Function(
List<NotificationModel> notifications, int notificationsCount)?
loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
@@ -137,7 +139,7 @@ class _$NotificationsStateImpl implements _NotificationsState {
TResult Function() $default, {
required TResult Function() empty,
required TResult Function(
List<String> notifications, int notificationsCount)
List<NotificationModel> notifications, int notificationsCount)
loaded,
required TResult Function(String errorMessage) error,
}) {
@@ -149,7 +151,8 @@ class _$NotificationsStateImpl implements _NotificationsState {
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? empty,
TResult? Function(List<String> notifications, int notificationsCount)?
TResult? Function(
List<NotificationModel> notifications, int notificationsCount)?
loaded,
TResult? Function(String errorMessage)? error,
}) {
@@ -161,7 +164,8 @@ class _$NotificationsStateImpl implements _NotificationsState {
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? empty,
TResult Function(List<String> notifications, int notificationsCount)?
TResult Function(
List<NotificationModel> notifications, int notificationsCount)?
loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
@@ -262,7 +266,7 @@ class _$NotificationsStateEmptyImpl implements NotificationsStateEmpty {
TResult Function() $default, {
required TResult Function() empty,
required TResult Function(
List<String> notifications, int notificationsCount)
List<NotificationModel> notifications, int notificationsCount)
loaded,
required TResult Function(String errorMessage) error,
}) {
@@ -274,7 +278,8 @@ class _$NotificationsStateEmptyImpl implements NotificationsStateEmpty {
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? empty,
TResult? Function(List<String> notifications, int notificationsCount)?
TResult? Function(
List<NotificationModel> notifications, int notificationsCount)?
loaded,
TResult? Function(String errorMessage)? error,
}) {
@@ -286,7 +291,8 @@ class _$NotificationsStateEmptyImpl implements NotificationsStateEmpty {
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? empty,
TResult Function(List<String> notifications, int notificationsCount)?
TResult Function(
List<NotificationModel> notifications, int notificationsCount)?
loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
@@ -346,7 +352,7 @@ abstract class _$$NotificationsStateLoadedImplCopyWith<$Res> {
$Res Function(_$NotificationsStateLoadedImpl) then) =
__$$NotificationsStateLoadedImplCopyWithImpl<$Res>;
@useResult
$Res call({List<String> notifications, int notificationsCount});
$Res call({List<NotificationModel> notifications, int notificationsCount});
}
/// @nodoc
@@ -371,7 +377,7 @@ class __$$NotificationsStateLoadedImplCopyWithImpl<$Res>
notifications: null == notifications
? _value._notifications
: notifications // ignore: cast_nullable_to_non_nullable
as List<String>,
as List<NotificationModel>,
notificationsCount: null == notificationsCount
? _value.notificationsCount
: notificationsCount // ignore: cast_nullable_to_non_nullable
@@ -384,13 +390,13 @@ class __$$NotificationsStateLoadedImplCopyWithImpl<$Res>
class _$NotificationsStateLoadedImpl implements NotificationsStateLoaded {
const _$NotificationsStateLoadedImpl(
{required final List<String> notifications,
{required final List<NotificationModel> notifications,
required this.notificationsCount})
: _notifications = notifications;
final List<String> _notifications;
final List<NotificationModel> _notifications;
@override
List<String> get notifications {
List<NotificationModel> get notifications {
if (_notifications is EqualUnmodifiableListView) return _notifications;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_notifications);
@@ -434,7 +440,7 @@ class _$NotificationsStateLoadedImpl implements NotificationsStateLoaded {
TResult Function() $default, {
required TResult Function() empty,
required TResult Function(
List<String> notifications, int notificationsCount)
List<NotificationModel> notifications, int notificationsCount)
loaded,
required TResult Function(String errorMessage) error,
}) {
@@ -446,7 +452,8 @@ class _$NotificationsStateLoadedImpl implements NotificationsStateLoaded {
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? empty,
TResult? Function(List<String> notifications, int notificationsCount)?
TResult? Function(
List<NotificationModel> notifications, int notificationsCount)?
loaded,
TResult? Function(String errorMessage)? error,
}) {
@@ -458,7 +465,8 @@ class _$NotificationsStateLoadedImpl implements NotificationsStateLoaded {
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? empty,
TResult Function(List<String> notifications, int notificationsCount)?
TResult Function(
List<NotificationModel> notifications, int notificationsCount)?
loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
@@ -509,10 +517,10 @@ class _$NotificationsStateLoadedImpl implements NotificationsStateLoaded {
abstract class NotificationsStateLoaded implements NotificationsState {
const factory NotificationsStateLoaded(
{required final List<String> notifications,
{required final List<NotificationModel> notifications,
required final int notificationsCount}) = _$NotificationsStateLoadedImpl;
List<String> get notifications;
List<NotificationModel> get notifications;
int get notificationsCount;
/// Create a copy of NotificationsState
@@ -598,7 +606,7 @@ class _$NotificationsStateErrorImpl implements NotificationsStateError {
TResult Function() $default, {
required TResult Function() empty,
required TResult Function(
List<String> notifications, int notificationsCount)
List<NotificationModel> notifications, int notificationsCount)
loaded,
required TResult Function(String errorMessage) error,
}) {
@@ -610,7 +618,8 @@ class _$NotificationsStateErrorImpl implements NotificationsStateError {
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? empty,
TResult? Function(List<String> notifications, int notificationsCount)?
TResult? Function(
List<NotificationModel> notifications, int notificationsCount)?
loaded,
TResult? Function(String errorMessage)? error,
}) {
@@ -622,7 +631,8 @@ class _$NotificationsStateErrorImpl implements NotificationsStateError {
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? empty,
TResult Function(List<String> notifications, int notificationsCount)?
TResult Function(
List<NotificationModel> notifications, int notificationsCount)?
loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),

View File

@@ -0,0 +1,34 @@
import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart';
import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_state.dart';
import 'package:eatvio_app/features/notifications/presentation/widgets/notification_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class NotificationsScreen extends StatelessWidget {
const NotificationsScreen({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<NotificationsCubit, NotificationsState>(
builder: (BuildContext context, NotificationsState state) {
return Scaffold(
appBar: AppBar(
title: const Text('Уведомления'),
),
body: state is NotificationsStateLoaded
? CustomScrollView(
slivers: [
SliverList.builder(
itemBuilder: (context, index) => NotificationWidget(
notification: state.notifications[index],
),
itemCount: state.notifications.length,
)
],
)
: const Placeholder(),
);
},
);
}
}

View File

@@ -1,27 +0,0 @@
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/notifications_cubit/notifications_cubit.dart';
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/notifications_cubit/notifications_state.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class NotificationsScreen extends StatelessWidget {
const NotificationsScreen({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<NotificationsCubit, NotificationsState>(
builder: (BuildContext context, NotificationsState state) {
return Scaffold(
appBar: AppBar(
title: const Text('Уведомления'),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Text(
state is NotificationsStateLoaded ? state.notifications.toString() : 'Уведомлений нет',
),
),
);
},
);
}
}

View File

@@ -0,0 +1,15 @@
import 'package:eatvio_app/features/notifications/models/notification_model.dart';
import 'package:flutter/material.dart';
class NotificationWidget extends StatelessWidget {
const NotificationWidget({super.key, required this.notification});
final NotificationModel notification;
@override
Widget build(BuildContext context) {
// return (notification.type) {
// NotificationType.userSubscribedNotification => const _UserSubscribedNotification(),
// };
return Container();
}
}

View File

@@ -20,10 +20,11 @@ class OrderInfoCubit extends Cubit<OrderInfoState> {
}
}
Future<void> getChosenPlan(int id) async{
Future<void> getChosenPlan(int id) async {
try {
emit(state.copyWith(isLoading: true));
emit(state.copyWith(isLoading: false));
final chosenPlan = await chatRepository.getChosenPlan(id: id);
emit(state.copyWith(isLoading: false, chosenPlan: chosenPlan));
} catch (e) {
log(e.toString());
emit(state.copyWith(isLoading: false));

View File

@@ -7,7 +7,7 @@ part 'order_info_state.freezed.dart';
class OrderInfoState with _$OrderInfoState {
factory OrderInfoState({
@Default(false) bool isLoading,
@Default(true) bool isLoading,
@Default(null) ChosenPlanModel? chosenPlan,
}) = _OrderInfoState;

View File

@@ -128,7 +128,7 @@ class __$$OrderInfoStateImplCopyWithImpl<$Res>
/// @nodoc
class _$OrderInfoStateImpl implements _OrderInfoState {
_$OrderInfoStateImpl({this.isLoading = false, this.chosenPlan = null});
_$OrderInfoStateImpl({this.isLoading = true, this.chosenPlan = null});
@override
@JsonKey()

View File

@@ -1,6 +1,8 @@
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/features/cancel_plan_bottom_sheet/cancel_plan_bottom_sheet.dart';
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart';
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
@@ -117,55 +119,72 @@ class _InfoByOrderScreenContentState extends State<InfoByOrderScreenContent> {
planStatus = PlanStatusValues.pendingPayment;
}
return Scaffold(
backgroundColor: appTheme.gray100,
appBar: AppBar(
title: Text(
s.infoAboutOrder,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
GoRouter.of(context).pop();
}),
backgroundColor: appTheme.gray100,
appBar: AppBar(
title: Text(
s.infoAboutOrder,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
body: SingleChildScrollView(
child: Column(
children: [
if (widget.successfulPayment != null) ...[
const SizedBox(height: 8),
PaymentBanner(
successfulPayment: widget.successfulPayment ?? false,
),
],
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
GoRouter.of(context).pop();
}),
),
body: SingleChildScrollView(
child: Column(
children: [
if (widget.successfulPayment != null) ...[
const SizedBox(height: 8),
AuthorCard(
author: plan.plan.author,
title: s.plan_seller,
titleStyle: TextStyles.textSSemibold.copyWith(color: appTheme.gray),
backgroundColor: appTheme.whiteA700,
PaymentBanner(
successfulPayment: widget.successfulPayment ?? false,
),
const SizedBox(height: 8),
PlanProps(planDuration: plan.plan.duration, planPrice: chosenOption?.price.toDouble() ?? 0, planStatus: planStatus),
const SizedBox(height: 8),
PlanInfo(
title: plan.plan.name,
description: plan.plan.details,
imagePath: plan.plan.image,
),
const SizedBox(height: 8),
OptionWidget(
option: chosenOption,
disableDuration: true,
),
const SizedBox(height: 8),
PlanState(
state: plan.chosenPlanState,
),
const SizedBox(height: 30),
],
),
));
const SizedBox(height: 8),
AuthorCard(
author: plan.plan.author,
title: s.plan_seller,
titleStyle: TextStyles.textSSemibold.copyWith(color: appTheme.gray),
backgroundColor: appTheme.whiteA700,
),
const SizedBox(height: 8),
PlanProps(planDuration: plan.plan.duration, planPrice: chosenOption?.price.toDouble() ?? 0, planStatus: planStatus),
const SizedBox(height: 8),
PlanInfo(
title: plan.plan.name,
description: plan.plan.details,
imagePath: plan.plan.image,
),
const SizedBox(height: 8),
OptionWidget(
option: chosenOption,
disableDuration: true,
),
const SizedBox(height: 8),
PlanState(
state: plan.chosenPlanState,
),
const SizedBox(height: 20),
if (widget.successfulPayment == null)
CustomOutlinedButton(
text: s.cancelConsultation,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.strongRed),
buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.lightRed)),
onPressed: () async {
showModalBottomSheet(
context: context,
builder: (context) => CancelPlanBottomSheet(
enableForm: plan.chosenPlanState.isStateAfterPaidState(),
planId: plan.id,
),
);
},
)
],
),
),
);
},
);
}

View File

@@ -142,20 +142,6 @@ class _PaymentBannerState extends State<PaymentBanner> {
],
),
)
])
// ? Column(children: [
// SizedBox(
// width: size.width * 0.6,
// child: Text(
// s.smth_went_wrong_payment_failed,
// style: TextStyles.textLSemibold.copyWith(color: appTheme.black),
// ),
// ),
// ])
// : Padding(
// padding: const EdgeInsets.only(right: 16),
// child: ,
// ),
);
]));
}
}

View File

@@ -17,7 +17,7 @@ class PlanStatusBox extends StatelessWidget {
final s = S.of(context);
final title = switch (status) {
PlanStatusValues.paid => s.order_paid,
PlanStatusValues.pendingPayment => s.payment_failed,
PlanStatusValues.pendingPayment => s.pending_payment,
PlanStatusValues.paymentFailed => s.payment_failed,
};
return Container(

View File

@@ -39,6 +39,19 @@ enum ChosenPlanStateEnum {
ChosenPlanStateEnum.stateLastDayStarted,
ChosenPlanStateEnum.stateLastDayFinished,
ChosenPlanStateEnum.stateDone,
ChosenPlanStateEnum.stateInProgress,
];
}
bool isStateAfterPaidState() {
switch (this) {
case ChosenPlanStateEnum.stateInited:
case ChosenPlanStateEnum.stateInCoordination:
case ChosenPlanStateEnum.stateStartDateHasBeenSet:
case ChosenPlanStateEnum.statePendingPayment:
return false;
default:
return true;
}
}
}

View File

@@ -128,21 +128,24 @@ class AboutPlanCubit extends Cubit<AboutPlanState> {
);
}
Future<ChosenPlanModel?> chosePlan() async {
Future<(ChosenPlanModel?, String?)> chosePlan() async {
try {
final slug = state.planModel?.slug;
final optionIndex = state.chosenOptionIndex;
if (slug != null) {
final optionName = _getOptionNameFromIndex(optionIndex);
final body = {'options_type': optionName};
return await planRepository.chosePlan(slug, body);
return (await planRepository.chosePlan(slug, body), null);
}
} on DioException catch (e, st) {
log(e.toString(), stackTrace: st);
if (e.response?.statusCode == 400) {
return (null, 'hasPlan');
}
log(e.toString(), stackTrace: st);
} catch (e, st) {
log(e.toString(), stackTrace: st);
}
return null;
return (null, '');
}
String _getOptionNameFromIndex(int index) => switch (index) {

View File

@@ -220,7 +220,11 @@ class _AboutPlanScreenState extends State<AboutPlanScreen> {
),
backgroundColor: appTheme.greenA700,
onPressed: () async {
final chosenPlan = await context.read<AboutPlanCubit>().chosePlan();
final (chosenPlan, error) = await context.read<AboutPlanCubit>().chosePlan();
if (error == 'hasPlan') {
showErrorDialog(context, title: s.end_previous_plan, description: s.you_can_not_start_plan_until_end_of_prev_one);
return;
}
if (chosenPlan == null) {
if (!context.mounted) return;
showErrorDialog(context, title: 'Не удалось заказать план');

View File

@@ -75,6 +75,10 @@ class MessageLookup extends MessageLookupByLibrary {
"can_not_see_next_day": MessageLookupByLibrary.simpleMessage(
"Просмотр последующего дня возможен только при прохождении диеты",
),
"cancel": MessageLookupByLibrary.simpleMessage("Отменить"),
"cancelConsultation": MessageLookupByLibrary.simpleMessage(
"Отменить консультацию",
),
"cancel_diet": MessageLookupByLibrary.simpleMessage("Отменить диету?"),
"cancel_order": MessageLookupByLibrary.simpleMessage("Отменить заказ"),
"carb_type": MessageLookupByLibrary.simpleMessage("Мучное"),
@@ -117,6 +121,9 @@ class MessageLookup extends MessageLookupByLibrary {
"city_of_living": MessageLookupByLibrary.simpleMessage("Город проживания"),
"comfort": MessageLookupByLibrary.simpleMessage("Комфорт"),
"comment": MessageLookupByLibrary.simpleMessage("комментарий"),
"commentForMeal": MessageLookupByLibrary.simpleMessage(
"Прокомментировать прием пищи",
),
"comments": MessageLookupByLibrary.simpleMessage("Комментарии"),
"comments_plural": MessageLookupByLibrary.simpleMessage("комментариев"),
"complain": MessageLookupByLibrary.simpleMessage("Пожаловаться"),
@@ -141,6 +148,9 @@ class MessageLookup extends MessageLookupByLibrary {
"copy_referral_link": MessageLookupByLibrary.simpleMessage(
"Скопировать пригласительную ссылку",
),
"couldNotCancelOrder": MessageLookupByLibrary.simpleMessage(
"Не удалось отменить заказ",
),
"could_not_cancel_diet": MessageLookupByLibrary.simpleMessage(
"Не удалось отменить диету",
),
@@ -181,6 +191,12 @@ class MessageLookup extends MessageLookupByLibrary {
),
"date_of_birth": MessageLookupByLibrary.simpleMessage("Дата рождения"),
"day": MessageLookupByLibrary.simpleMessage("День"),
"dayComment": MessageLookupByLibrary.simpleMessage(
"Комментарий ко всему дню",
),
"dayCommentTitle": MessageLookupByLibrary.simpleMessage(
"Прокомментировать день",
),
"days": MessageLookupByLibrary.simpleMessage("дней"),
"deception": MessageLookupByLibrary.simpleMessage("Обман"),
"deleted": MessageLookupByLibrary.simpleMessage("Удалена"),
@@ -228,6 +244,9 @@ class MessageLookup extends MessageLookupByLibrary {
"email_invalid": MessageLookupByLibrary.simpleMessage(
"Некорректный адрес электронной почты",
),
"end_previous_plan": MessageLookupByLibrary.simpleMessage(
"Закончите прохождение предыдущего плана",
),
"enter_additional_information": MessageLookupByLibrary.simpleMessage(
"Введите любую дополнительную информацию на своё усмотрение:",
),
@@ -242,6 +261,9 @@ class MessageLookup extends MessageLookupByLibrary {
),
"exit": MessageLookupByLibrary.simpleMessage("Выйти"),
"expand": MessageLookupByLibrary.simpleMessage("Показать полностью"),
"explainResonOfCancelation": MessageLookupByLibrary.simpleMessage(
"Опишите причину отказа",
),
"explicitImage": MessageLookupByLibrary.simpleMessage(
"Откровенное изображение",
),
@@ -300,6 +322,9 @@ class MessageLookup extends MessageLookupByLibrary {
"Склонность к гипертонии",
),
"has_hypo": MessageLookupByLibrary.simpleMessage("Склонность к гипотонии"),
"has_not_filled_it_yet": MessageLookupByLibrary.simpleMessage(
"ещё не закончил(a) этот день",
),
"hatred": MessageLookupByLibrary.simpleMessage("Оскорбление"),
"hatred_description": MessageLookupByLibrary.simpleMessage(
"Враждебные высказывания",
@@ -348,6 +373,9 @@ class MessageLookup extends MessageLookupByLibrary {
"lower": MessageLookupByLibrary.simpleMessage("Нижнее"),
"male": MessageLookupByLibrary.simpleMessage("Мужской"),
"meal": MessageLookupByLibrary.simpleMessage("Приём пищи"),
"mealComment": MessageLookupByLibrary.simpleMessage(
"Комментарий к приему пищи",
),
"meat": MessageLookupByLibrary.simpleMessage("Мясо"),
"meat_type": MessageLookupByLibrary.simpleMessage("С мясом"),
"meat_type_desc": MessageLookupByLibrary.simpleMessage(
@@ -414,10 +442,12 @@ class MessageLookup extends MessageLookupByLibrary {
),
"option_title": MessageLookupByLibrary.simpleMessage("Заголовок опции"),
"order": MessageLookupByLibrary.simpleMessage("Порядок"),
"orderCanceled": MessageLookupByLibrary.simpleMessage("Заказ отменен"),
"order_created": MessageLookupByLibrary.simpleMessage("Заказ создан"),
"order_paid": MessageLookupByLibrary.simpleMessage("Заказ оплачен"),
"order_price": MessageLookupByLibrary.simpleMessage("Сумма заказа"),
"order_status": MessageLookupByLibrary.simpleMessage("Статус заказа"),
"orederCancelation": MessageLookupByLibrary.simpleMessage("Отмена заказа"),
"other": MessageLookupByLibrary.simpleMessage("Другое"),
"other_info": MessageLookupByLibrary.simpleMessage("Прочая информация"),
"password": MessageLookupByLibrary.simpleMessage("Пароль"),
@@ -436,6 +466,7 @@ class MessageLookup extends MessageLookupByLibrary {
"pay": MessageLookupByLibrary.simpleMessage("Оплатить"),
"payment_failed": MessageLookupByLibrary.simpleMessage("Оплата не прошла"),
"pcs": MessageLookupByLibrary.simpleMessage("шт"),
"pending_payment": MessageLookupByLibrary.simpleMessage("Ожидается оплата"),
"personal": MessageLookupByLibrary.simpleMessage("Персональные"),
"personal_diet_state": MessageLookupByLibrary.simpleMessage("Персональная"),
"photo": MessageLookupByLibrary.simpleMessage("Фото"),
@@ -494,6 +525,7 @@ class MessageLookup extends MessageLookupByLibrary {
"rating_history": MessageLookupByLibrary.simpleMessage(
"Начисление рейтинга",
),
"reasonExample": MessageLookupByLibrary.simpleMessage("Заказал по ошибке"),
"recipe_author": MessageLookupByLibrary.simpleMessage("Автор рецепта"),
"recipe_publishing": MessageLookupByLibrary.simpleMessage(
"Публикация рецепта",
@@ -505,6 +537,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Снять с публикации",
),
"save": MessageLookupByLibrary.simpleMessage("Сохранить"),
"send": MessageLookupByLibrary.simpleMessage("Отправить"),
"send_complain": MessageLookupByLibrary.simpleMessage("Отправить жалобу"),
"services": MessageLookupByLibrary.simpleMessage("Оказание услуг"),
"settings": MessageLookupByLibrary.simpleMessage("Настройки"),
@@ -572,6 +605,18 @@ class MessageLookup extends MessageLookupByLibrary {
"to_order": MessageLookupByLibrary.simpleMessage("Заказать"),
"try_again": MessageLookupByLibrary.simpleMessage("Попробовать снова"),
"try_again_2": MessageLookupByLibrary.simpleMessage("Попробуйте ещё раз"),
"typeAmoutOfReturn": MessageLookupByLibrary.simpleMessage(
"Укажите сумму которую вы хотите вернуть",
),
"typeDayComment": MessageLookupByLibrary.simpleMessage(
"Как можно подробнее прокомментируйте дневник пользователя",
),
"typeMealComment": MessageLookupByLibrary.simpleMessage(
"Как можно подробнее прокомментируйте прием пищи",
),
"typePortionComment": MessageLookupByLibrary.simpleMessage(
"Прокомментируйте блюдо",
),
"type_here_your_complaint": MessageLookupByLibrary.simpleMessage(
"Напишите здесь свою жалобу",
),
@@ -628,6 +673,16 @@ class MessageLookup extends MessageLookupByLibrary {
"wrong_credentials": MessageLookupByLibrary.simpleMessage(
"Неверные данные пользователя",
),
"youShureYouWantToCancelOrder": MessageLookupByLibrary.simpleMessage(
"Вы уверены, что хотите отменить заказ?",
),
"you_can_not_open_this_day": MessageLookupByLibrary.simpleMessage(
"Вы не можете перейти в этот день",
),
"you_can_not_start_plan_until_end_of_prev_one":
MessageLookupByLibrary.simpleMessage(
"Вы не можете начать план, пока не закончите прохождение предыдущего",
),
"you_will_get": MessageLookupByLibrary.simpleMessage("Вы получите"),
"your_blood_pressure": MessageLookupByLibrary.simpleMessage(
"Ваше артериальное давление?",

View File

@@ -2640,6 +2640,16 @@ class S {
);
}
/// `Ожидается оплата`
String get pending_payment {
return Intl.message(
'Ожидается оплата',
name: 'pending_payment',
desc: '',
args: [],
);
}
/// `Мои карты`
String get my_cards {
return Intl.message('Мои карты', name: 'my_cards', desc: '', args: []);
@@ -2844,6 +2854,206 @@ class S {
args: [],
);
}
/// `Вы не можете начать план, пока не закончите прохождение предыдущего`
String get you_can_not_start_plan_until_end_of_prev_one {
return Intl.message(
'Вы не можете начать план, пока не закончите прохождение предыдущего',
name: 'you_can_not_start_plan_until_end_of_prev_one',
desc: '',
args: [],
);
}
/// `Закончите прохождение предыдущего плана`
String get end_previous_plan {
return Intl.message(
'Закончите прохождение предыдущего плана',
name: 'end_previous_plan',
desc: '',
args: [],
);
}
/// `Вы не можете перейти в этот день`
String get you_can_not_open_this_day {
return Intl.message(
'Вы не можете перейти в этот день',
name: 'you_can_not_open_this_day',
desc: '',
args: [],
);
}
/// `ещё не закончил(a) этот день`
String get has_not_filled_it_yet {
return Intl.message(
'ещё не закончил(a) этот день',
name: 'has_not_filled_it_yet',
desc: '',
args: [],
);
}
/// `Как можно подробнее прокомментируйте прием пищи`
String get typeMealComment {
return Intl.message(
'Как можно подробнее прокомментируйте прием пищи',
name: 'typeMealComment',
desc: '',
args: [],
);
}
/// `Прокомментируйте блюдо`
String get typePortionComment {
return Intl.message(
'Прокомментируйте блюдо',
name: 'typePortionComment',
desc: '',
args: [],
);
}
/// `Прокомментировать прием пищи`
String get commentForMeal {
return Intl.message(
'Прокомментировать прием пищи',
name: 'commentForMeal',
desc: '',
args: [],
);
}
/// `Отправить`
String get send {
return Intl.message('Отправить', name: 'send', desc: '', args: []);
}
/// `Прокомментировать день`
String get dayCommentTitle {
return Intl.message(
'Прокомментировать день',
name: 'dayCommentTitle',
desc: '',
args: [],
);
}
/// `Как можно подробнее прокомментируйте дневник пользователя`
String get typeDayComment {
return Intl.message(
'Как можно подробнее прокомментируйте дневник пользователя',
name: 'typeDayComment',
desc: '',
args: [],
);
}
/// `Отменить консультацию`
String get cancelConsultation {
return Intl.message(
'Отменить консультацию',
name: 'cancelConsultation',
desc: '',
args: [],
);
}
/// `Отмена заказа`
String get orederCancelation {
return Intl.message(
'Отмена заказа',
name: 'orederCancelation',
desc: '',
args: [],
);
}
/// `Вы уверены, что хотите отменить заказ?`
String get youShureYouWantToCancelOrder {
return Intl.message(
'Вы уверены, что хотите отменить заказ?',
name: 'youShureYouWantToCancelOrder',
desc: '',
args: [],
);
}
/// `Опишите причину отказа`
String get explainResonOfCancelation {
return Intl.message(
'Опишите причину отказа',
name: 'explainResonOfCancelation',
desc: '',
args: [],
);
}
/// `Заказал по ошибке`
String get reasonExample {
return Intl.message(
'Заказал по ошибке',
name: 'reasonExample',
desc: '',
args: [],
);
}
/// `Укажите сумму которую вы хотите вернуть`
String get typeAmoutOfReturn {
return Intl.message(
'Укажите сумму которую вы хотите вернуть',
name: 'typeAmoutOfReturn',
desc: '',
args: [],
);
}
/// `Отменить`
String get cancel {
return Intl.message('Отменить', name: 'cancel', desc: '', args: []);
}
/// `Заказ отменен`
String get orderCanceled {
return Intl.message(
'Заказ отменен',
name: 'orderCanceled',
desc: '',
args: [],
);
}
/// `Не удалось отменить заказ`
String get couldNotCancelOrder {
return Intl.message(
'Не удалось отменить заказ',
name: 'couldNotCancelOrder',
desc: '',
args: [],
);
}
/// `Комментарий ко всему дню`
String get dayComment {
return Intl.message(
'Комментарий ко всему дню',
name: 'dayComment',
desc: '',
args: [],
);
}
/// `Комментарий к приему пищи`
String get mealComment {
return Intl.message(
'Комментарий к приему пищи',
name: 'mealComment',
desc: '',
args: [],
);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<S> {

Some files were not shown because too many files have changed in this diff Show More