feat(chats,diary): added logic for chosen diet portions and added banner to finish plan to chats

This commit is contained in:
Username1548
2025-04-30 21:25:23 +07:00
parent e572614a8b
commit 3b4ed501fb
28 changed files with 552 additions and 71 deletions

View File

@@ -386,12 +386,10 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_TEAM = "VL68C36HYS";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = VL68C36HYS;
DEVELOPMENT_TEAM = "";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -411,8 +409,7 @@
MARKETING_VERSION = 1.0.50;
PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "Distribution";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Distribution;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
@@ -539,12 +536,10 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_TEAM = "VL68C36HYS";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = VL68C36HYS;
DEVELOPMENT_TEAM = "";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -564,8 +559,7 @@
MARKETING_VERSION = 1.0.50;
PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "Distribution";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Distribution;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@@ -580,12 +574,10 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_TEAM = "VL68C36HYS";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = VL68C36HYS;
DEVELOPMENT_TEAM = "";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -605,8 +597,7 @@
MARKETING_VERSION = 1.0.50;
PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "Distribution";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Distribution;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";

View File

@@ -223,7 +223,7 @@ class AppRouter {
builder: (context, state) {
final data = state.extra as Map<String, dynamic>;
final chatId = data['chatId'] as String;
final sendInitialMessage = data['sendInitialMessage'] as bool;
final sendInitialMessage = data['sendInitialMessage'] as bool?;
return ChatScreen(chatId: chatId, sendInitialMessage: sendInitialMessage,);
},
),

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

@@ -98,11 +98,11 @@ Future<void> init() async {
sl.registerLazySingleton<WsManager>(() => WsManager(wsService: sl()));
sl.registerLazySingleton<NewMessagesCubit>(() => NewMessagesCubit());
sl.registerLazySingleton<NotificationsCubit>(() => NotificationsCubit(wsManager: sl()));
sl.registerLazySingleton<FeedCubit>(() => FeedCubit(feedRepository: sl()));
sl.registerLazySingleton<ProfileAvatarCubit>(() => ProfileAvatarCubit(profileRepository: sl()));
sl.registerLazySingleton<ChatsCubit>(() => ChatsCubit(chatRepository: sl(), wsManager: sl(), profileRepository: sl()));
// Cubit factories
sl.registerFactory<FeedCubit>(() => FeedCubit(feedRepository: sl()));
sl.registerFactory<RegistrationCubit>(() => RegistrationCubit(registrationRepository: sl()));
sl.registerFactory<ChatRoomCubit>(() => ChatRoomCubit(cardsRepository: sl(), chatRepository: sl(), wsManager: sl()));
sl.registerFactory<RecipesCubit>(() => RecipesCubit(feedRepository: sl(), recipeRepository: sl(), aboutRecipeRepository: sl()));
@@ -114,7 +114,7 @@ Future<void> init() async {
sl.registerFactory<PlansCubit>(() => PlansCubit(feedRepository: sl(), planRepository: sl()));
sl.registerFactory<SettingsCubit>(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl()));
sl.registerFactory<RegisterConsultantCubit>(() => RegisterConsultantCubit(consultantRepository: sl()));
sl.registerFactory<DiaryCubit>(() => DiaryCubit(diaryRepository: sl(), glossaryRepository: sl()));
sl.registerFactory<DiaryCubit>(() => DiaryCubit(diaryRepository: sl(), glossaryRepository: sl(), profileRepository: sl()));
sl.registerFactory<AboutDietCubit>(() => AboutDietCubit(dietsRepository: sl(), profileRepository: sl()));
sl.registerFactory<BlogPostCreateCubit>(() => BlogPostCreateCubit(blogPostRepository: sl()));
sl.registerFactory<SubCubit>(() => SubCubit(profileRepository: sl()));

View File

@@ -6,7 +6,7 @@ import 'package:flutter/material.dart';
Future<void> showBottomSheetWithBlur({
required BuildContext context,
required Widget child,
double height = 200,
double? height = 200,
bool hasBlur = true,
bool enableDrag = true,
bool userRootNavigator = false,

View File

@@ -0,0 +1,14 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'plan_complain_model.freezed.dart';
part 'plan_complain_model.g.dart';
@freezed
class PlanComplainModel with _$PlanComplainModel {
factory PlanComplainModel({
@JsonKey(name: 'description') required String description,
}) = _PlanComplainModel;
factory PlanComplainModel.fromJson(Map<String, dynamic> json) => _$PlanComplainModelFromJson(json);
}

View File

@@ -10,6 +10,7 @@ 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/core/websockets/ws_manager.dart';
import 'package:eatvio_app/features/chat/models/chat_message_model.dart';
import 'package:eatvio_app/features/chat/models/plan_complain_model.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.dart';
import 'package:eatvio_app/features/chat/repositories/chat_repository.dart';
import 'package:eatvio_app/features/my_finance/models/card_model.dart';
@@ -92,7 +93,7 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
hasCard: false,
),
);
if (chatRoom.chosenPlan?.consultantId == chatRoom.users.receiver.id) {
await checkCards();
}
@@ -209,6 +210,30 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
}
}
Future<bool> complainAboutConsultation({required String description}) async {
try {
final planId = (state as ChatRoomStateLoaded).chatRoom.chosenPlan?.id;
if (planId == null) return false;
await chatRepository.complainAboutConsultation(id: planId, body: PlanComplainModel(description: description));
return true;
} catch (e) {
log(e.toString());
return false;
}
}
Future<bool> finishChosenPlan() async {
try {
final planId = (state as ChatRoomStateLoaded).chatRoom.chosenPlan?.id;
if (planId == null) return false;
await chatRepository.finishChosenPlan(id: planId);
return true;
} catch (e) {
log(e.toString());
return false;
}
}
@override
Future<void> close() async {
wsManager.unsubscribeDirectChatChannel();

View File

@@ -29,7 +29,7 @@ import 'package:visibility_detector/visibility_detector.dart';
class ChatScreen extends StatefulWidget {
final String chatId;
final bool sendInitialMessage;
final bool? sendInitialMessage;
const ChatScreen({required this.chatId, super.key, required this.sendInitialMessage});
@override
@@ -48,7 +48,7 @@ class _ChatScreenState extends State<ChatScreen> {
void initState() {
super.initState();
scrollController.addListener(_onScroll);
chatRoomCubit.initChat(widget.chatId, sendInitialMessage: widget.sendInitialMessage);
chatRoomCubit.initChat(widget.chatId, sendInitialMessage: widget.sendInitialMessage ?? false);
}
_onScroll() {
@@ -98,7 +98,7 @@ class _ChatScreenState extends State<ChatScreen> {
BannerType? bannerType;
if (chosenPlan == null) {
bannerType = null;
} else if (!amIConsultant && !state.hasCard) {
} else if (amIConsultant && !state.hasCard) {
bannerType = BannerType.card;
} else if (amIConsultant && chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateInCoordination) {
bannerType = BannerType.date;
@@ -107,6 +107,8 @@ class _ChatScreenState extends State<ChatScreen> {
bannerType = BannerType.payment;
} else if (chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateDone) {
bannerType = BannerType.collectCharge;
} else if (!(chosenPlan.chosenPlanState == ChosenPlanStateEnum.stateLastDayFinished) && !amIConsultant) {
bannerType = BannerType.finishPlan;
}
PacketOptions? chosenOption;

View File

@@ -0,0 +1,102 @@
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/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class CompletePlanBottomSheet extends StatefulWidget {
const CompletePlanBottomSheet({super.key, required this.cubit});
final ChatRoomCubit cubit;
@override
State<CompletePlanBottomSheet> createState() => _CompletePlanBottomSheetState();
}
class _CompletePlanBottomSheetState extends State<CompletePlanBottomSheet> {
bool? isFailure;
@override
Widget build(BuildContext context) {
final s = S.of(context);
if (isFailure == true) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(
height: 8,
),
const BottomSheetHandlerBar(),
const SizedBox(
height: 20,
),
Text('Произошла ошибка, попробуйте позже', style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
const SizedBox(
height: 8,
),
CustomOutlinedButton(
text: s.ok,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.blueA400)),
onPressed: () async {
context.pop();
},
),
const SizedBox(
height: 40,
),
],
);
}
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(
height: 8,
),
const BottomSheetHandlerBar(),
const SizedBox(
height: 20,
),
Text('Подтверждение плана', style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black)),
const SizedBox(
height: 16,
),
Text(
'Вы уверенны что хотите подтвердить прохождения плана?',
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
textAlign: TextAlign.center,
),
const SizedBox(
height: 8,
),
CustomOutlinedButton(
text: s.confirm,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.blueA400)),
onPressed: () async {
final result = await widget.cubit.finishChosenPlan();
if (result) {
if (context.mounted) {
context.pop();
return;
}
}
setState(() {
isFailure = true;
});
},
),
const SizedBox(
height: 40,
),
],
),
);
}
}

View File

@@ -1,11 +1,14 @@
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/bottom_sheet_with_blur.dart';
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.dart';
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/chat/presentation/widgets/complete_plan_bottom_sheet.dart';
import 'package:eatvio_app/features/chat/presentation/widgets/plan_complain_bottom_sheet.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';
@@ -40,7 +43,7 @@ class _NotificationBannerState extends State<NotificationBanner> {
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (widget.bannerType != BannerType.collectCharge)
if (widget.bannerType != BannerType.collectCharge && widget.bannerType != BannerType.finishPlan)
Padding(
padding: const EdgeInsets.only(top: 12, right: 8, left: 12),
child: SvgPicture.asset('assets/icons/reversed_exclamation_mark.svg', height: 68, width: 68),
@@ -139,6 +142,19 @@ class _NotificationBannerState extends State<NotificationBanner> {
),
const SizedBox(height: 8),
],
BannerType.finishPlan => [
SizedBox(
width: size.width * 0.6,
child: Text('${s.congratsYouHaveFinishedPlan} «${widget.planName}». ${s.pleaseConfirmPlan}',
style: TextStyles.textMMediumAuto.copyWith(color: appTheme.black)),
),
const SizedBox(height: 8),
SizedBox(
width: size.width * 0.7,
child: Text(s.planWillBeConfirmedAfterThreeDays, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
),
const SizedBox(height: 8),
],
},
),
),
@@ -156,7 +172,57 @@ class _NotificationBannerState extends State<NotificationBanner> {
GoRouter.of(context).push(CardSettingsScreen.path, extra: widget.chosenPlanId);
},
),
)
),
if (widget.bannerType == BannerType.finishPlan) ...[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: CustomOutlinedButton(
text: s.confirm,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.blueA400)),
onPressed: () async {
final cubit = context.read<ChatRoomCubit>();
showBottomSheetWithBlur(
userRootNavigator: true,
context: context,
height: null,
child: CompletePlanBottomSheet(
cubit: cubit,
),
);
},
),
),
const SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: SizedBox(
child: Text(s.youCanComplain, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
),
),
const SizedBox(height: 8),
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: GestureDetector(
onTap: () {
final cubit = context.read<ChatRoomCubit>();
showBottomSheetWithBlur(
userRootNavigator: true,
context: context,
height: null,
child: PlanComplainBottomSheet(
cubit: cubit,
),
);
},
child: Text(s.leaveComplainRequest, style: TextStyles.textSMedium.copyWith(color: appTheme.red))),
),
),
const SizedBox(height: 8),
]
],
),
);
@@ -176,4 +242,4 @@ class _StarIcon extends StatelessWidget {
}
}
enum BannerType { date, card, payment, collectCharge }
enum BannerType { date, card, payment, collectCharge, finishPlan }

View File

@@ -0,0 +1,154 @@
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/features/chat/presentation/bloc/chat_room_cubit/chat_room_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:go_router/go_router.dart';
class PlanComplainBottomSheet extends StatefulWidget {
const PlanComplainBottomSheet({super.key, required this.cubit});
final ChatRoomCubit cubit;
@override
State<PlanComplainBottomSheet> createState() => _PlanComplainBottomSheetState();
}
class _PlanComplainBottomSheetState extends State<PlanComplainBottomSheet> {
final controller = TextEditingController();
bool? result;
int? textLength;
@override
Widget build(BuildContext context) {
final s = S.of(context);
if (result == true) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(
height: 8,
),
const BottomSheetHandlerBar(),
const SizedBox(
height: 20,
),
Text('Подача жалобы', style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black)),
const SizedBox(
height: 16,
),
Text('Жалоба отправлена на рассмотрение. Обычно это занимает до трёх рабочих дней.', style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
const SizedBox(
height: 8,
),
CustomOutlinedButton(
text: s.ok,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.blueA400)),
onPressed: () async {
context.pop();
},
),
const SizedBox(
height: 40,
),
],
);
} else if (result == false) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(
height: 8,
),
const BottomSheetHandlerBar(),
const SizedBox(
height: 20,
),
Text('Подача жалобы', style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black)),
const SizedBox(
height: 16,
),
Text('Произошла ошибка, попробуйте позже', style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
const SizedBox(
height: 8,
),
CustomOutlinedButton(
text: s.ok,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.blueA400)),
onPressed: () async {
context.pop();
},
),
const SizedBox(
height: 40,
),
],
);
}
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(
height: 8,
),
const BottomSheetHandlerBar(),
const SizedBox(
height: 20,
),
Text('Подача жалобы', style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black)),
const SizedBox(
height: 16,
),
Align(alignment: Alignment.centerLeft, child: Text('Опишите причину жалобы', style: TextStyles.textSMedium.copyWith(color: appTheme.black))),
const SizedBox(
height: 8,
),
CustomTextFormField(
controller: controller,
hasError: textLength != null && textLength! < 3,
onChanged: (p0) {
setState(() {
textLength = p0.length;
});
},
),
if (textLength != null && textLength! < 3) ...[
const SizedBox(
height: 4,
),
ValidationErrorWidget(error: s.atLeast3Symbols),
],
const SizedBox(
height: 8,
),
CustomOutlinedButton(
text: s.confirm,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.blueA400)),
onPressed: () async {
if (controller.text.length < 3) {
return;
}
final result = await widget.cubit.complainAboutConsultation(description: controller.text);
setState(() {
this.result = result;
});
},
),
const SizedBox(
height: 40,
),
],
),
);
}
}

View File

@@ -6,6 +6,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/chat/models/plan_complain_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';
@@ -132,4 +133,25 @@ class ChatRepository {
rethrow;
}
}
Future<void> finishChosenPlan({
required int id,
}) async {
try {
await chatService.finishChosenPlan(id: id);
} catch (e) {
rethrow;
}
}
Future<void> complainAboutConsultation({
required int id,
required PlanComplainModel body,
}) async {
try {
await chatService.complainAboutConsultation(id: id, body: body);
} 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/chat/models/plan_complain_model.dart';
import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
import 'package:retrofit/retrofit.dart';
@@ -68,4 +69,15 @@ abstract class ChatService {
Future<ChosenPlanModel> getChosenPlan({
@Path() required int id,
});
@POST('/api/chosen_plans/{id}/confirm_finish')
Future<void> finishChosenPlan({
@Path() required int id,
});
@POST('/api/chosen_plans/{id}/complain')
Future<void> complainAboutConsultation({
@Path() required int id,
@Body() required PlanComplainModel body,
});
}

View File

@@ -37,8 +37,9 @@ class DiaryModel with _$DiaryModel {
}
DateTime parseUnformattedDate(String date) {
final dateChunks = date.split('/').map(int.parse).toList();
return DateTime(dateChunks.last, dateChunks.first, dateChunks[1]);
final dateChunks = date.split('-').map(int.parse).toList();
return DateTime(dateChunks.first, dateChunks[1], dateChunks.last);
}
enum DiaryStateEnum {

View File

@@ -8,7 +8,6 @@ part 'consultation_service.g.dart';
abstract class ConsultationService {
factory ConsultationService(Dio dio, {String baseUrl}) = _ConsultationService;
@GET('/api/consultations')
@GET('/api/consultations/in_progress')
Future<ConsultationPaginationModel> getConsultations();
}

View File

@@ -23,7 +23,10 @@ enum PortionState {
@freezed
class DiaryDayWrapperModel with _$DiaryDayWrapperModel {
const factory DiaryDayWrapperModel({
@JsonKey(name: "diary") required DiaryDayModel diary,
@JsonKey(
name: "diary",
)
required DiaryDayModel diary,
}) = _DiaryDayWrapperModel;
factory DiaryDayWrapperModel.fromJson(Map<String, dynamic> json) => _$DiaryDayWrapperModelFromJson(json);

View File

@@ -11,6 +11,7 @@ import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
import 'package:eatvio_app/features/diary/repositories/diary_repository.dart';
import 'package:eatvio_app/features/glossary/models/glossary_model.dart';
import 'package:eatvio_app/features/glossary/repositories/glossary_repository.dart';
import 'package:eatvio_app/features/profile/repositories/profile_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class DiaryCubit extends Cubit<DiaryState> {
@@ -19,8 +20,13 @@ class DiaryCubit extends Cubit<DiaryState> {
late int selectedMeal;
final DiaryRepository diaryRepository;
final GlossaryRepository glossaryRepository;
final ProfileRepository profileRepository;
int? diaryId;
DiaryCubit({required this.diaryRepository, required this.glossaryRepository}) : super(const DiaryState.loading()) {
DiaryCubit({
required this.diaryRepository,
required this.glossaryRepository,
required this.profileRepository,
}) : super(const DiaryState.loading()) {
today = DateTime.now();
selectedDay = today;
diaryRepository.forceFetchStream.listen((_) {
@@ -42,6 +48,7 @@ class DiaryCubit extends Cubit<DiaryState> {
fetchDiary([bool needLoading = false, int? diaryId]) async {
this.diaryId ??= diaryId;
await profileRepository.getChosenDiets();
if (needLoading) {
emit(const DiaryState.loading());
selectedMeal = 1;
@@ -88,7 +95,12 @@ class DiaryCubit extends Cubit<DiaryState> {
required String wakeUp,
required String retire,
}) async {
await diaryRepository.finishDiaryDay(diaryId: diaryId, wakeUp: wakeUp, retire: retire, comment: comment);
await diaryRepository.finishDiaryDay(
diaryId: diaryId,
wakeUp: wakeUp,
retire: retire,
comment: comment.isEmpty ? null : comment,
);
await fetchDiary();
}

View File

@@ -79,7 +79,12 @@ class DiaryScreenContent extends StatelessWidget {
child: Column(
children: [
if (diaryId == null) const CalendarWidget(),
if (state is DiaryStateLoading) const Loader(),
if (state is DiaryStateLoading) ...[
const SizedBox(
height: 60,
),
const Loader()
],
if (state is DiaryStateLoaded)
WakeupAndRetireWidget(
showRetire: state.diary.typeOfDiary.hasChosenPlan,

View File

@@ -34,7 +34,13 @@ class _ProductWidgetState extends State<ProductWidget> {
super.initState();
diaryCubit = context.read<DiaryCubit>();
portionName = widget.portion.portion == 'gr' ? 'гр' : 'шт';
isEaten = widget.portion.portionState == PortionState.eatenFromDiet ? true : false;
}
@override
void didUpdateWidget(ProductWidget oldWidget) {
super.didUpdateWidget(oldWidget);
portionName = widget.portion.portion == 'gr' ? 'гр' : 'шт';
isEaten = widget.portion.portionState == PortionState.eatenFromDiet ? true : false;
}
@@ -64,8 +70,9 @@ class _ProductWidgetState extends State<ProductWidget> {
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 canEditPortion = !state.isConsultantView && !isDiaryCommented && !isDiaryFinished && !widget.portion.fromDiet;
final consultantComments = state.consultantReview?.consultantComments;
final canMarkAsEaten = !state.isConsultantView && !isDiaryCommented && !isDiaryFinished;
PortionComment? portionComment;
if (consultantComments != null) {
portionComment = consultantComments.portionsComments.firstWhereOrNull((elem) => elem.portionId == widget.portion.id);
@@ -108,7 +115,7 @@ class _ProductWidgetState extends State<ProductWidget> {
});
},
child: SvgPicture.asset('assets/icons/edit.svg'));
} else if (!isDiaryCommented) {
} else if (!isDiaryCommented && !widget.portion.fromDiet) {
trailing = const Icon(Icons.more_vert_rounded);
}
return Column(
@@ -121,24 +128,26 @@ class _ProductWidgetState extends State<ProductWidget> {
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,
);
}
},
onChanged: canMarkAsEaten
? (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,
);
}
}
: null,
activeColor: appTheme.blueA400,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),

View File

@@ -30,9 +30,9 @@ class PlanModel with _$PlanModel {
@JsonKey(name: "type", fromJson: planTypeFromJson) required PlanType type,
@JsonKey(name: "description_md") required String details,
@JsonKey(name: "packets_count") required int packetsCount,
@JsonKey(name: "packet1_options") PacketOptions? packet1Options,
@JsonKey(name: "packet2_options") PacketOptions? packet2Options,
@JsonKey(name: "packet3_options") PacketOptions? packet3Options,
@JsonKey(name: "packet1_options", fromJson: parseOption) PacketOptions? packet1Options,
@JsonKey(name: "packet2_options", fromJson: parseOption) PacketOptions? packet2Options,
@JsonKey(name: "packet3_options", fromJson: parseOption) PacketOptions? packet3Options,
@JsonKey(name: "author") required Author author,
@JsonKey(name: "total_likes") required int totalLikes,
@JsonKey(name: "total_views") required int totalViews,
@@ -46,6 +46,13 @@ class PlanModel with _$PlanModel {
factory PlanModel.fromJson(Map<String, dynamic> json) => _$PlanModelFromJson(json);
}
PacketOptions? parseOption(json) {
if (json is Map<String, dynamic>) {
return PacketOptions.fromJson(json);
}
return null;
}
PlanType planTypeFromJson(dynamic json) {
if (json is List<dynamic>) {
return const PlanType(fit: false, slim: false, behavior: false, wellness: false);

View File

@@ -68,7 +68,7 @@ class _InternetConnectionCheckerScreenState extends State<InternetConnectionChec
final uris = [
Uri.parse('https://google.com'),
Uri.parse('https://yandex.ru'),
Uri.parse('https://dev2.eatvqqwewqeio.com'),
Uri.parse('https://dev2.eatvio.com'),
Uri.parse('https://eatvio.ru'),
];
for (final uri in uris) {

View File

@@ -157,7 +157,7 @@ class _InfoByOrderScreenContentState extends State<InfoByOrderScreenContent> {
const SizedBox(height: 8),
PlanProps(
planDuration: plan.plan.duration,
planPrice: chosenOption?.price ?? 0,
planPrice: chosenOption?.price.toDouble() ?? 0,
planStatus: planStatus),
const SizedBox(height: 8),
PlanInfo(

View File

@@ -45,6 +45,7 @@ enum ChosenPlanStateEnum {
bool isStateAfterPaidState() {
switch (this) {
case ChosenPlanStateEnum.stateCanceledByTheInitiator:
case ChosenPlanStateEnum.stateInited:
case ChosenPlanStateEnum.stateInCoordination:
case ChosenPlanStateEnum.stateStartDateHasBeenSet:

View File

@@ -42,7 +42,7 @@ class _WelcomeScreenState extends State<WelcomeScreen> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 40),
const SizedBox(height: 80),
SvgPicture.asset('assets/images/logo_welcome.svg'),
const Spacer(),
Padding(
@@ -70,7 +70,7 @@ class _WelcomeScreenState extends State<WelcomeScreen> {
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
height: 1,
height: 1.15,
),
textAlign: TextAlign.center,
),

View File

@@ -379,6 +379,9 @@ class MessageLookup extends MessageLookupByLibrary {
"Нарушение законодательства",
),
"lbl_english": MessageLookupByLibrary.simpleMessage("English"),
"leaveComplainRequest": MessageLookupByLibrary.simpleMessage(
"Подать жалобу",
),
"light_type": MessageLookupByLibrary.simpleMessage("Легкая"),
"light_type_desc": MessageLookupByLibrary.simpleMessage(
"С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками.",
@@ -501,6 +504,9 @@ class MessageLookup extends MessageLookupByLibrary {
"Физическая форма",
),
"place_of_living": MessageLookupByLibrary.simpleMessage("Место проживания"),
"planWillBeConfirmedAfterThreeDays": MessageLookupByLibrary.simpleMessage(
"Если вы не подтвердите, то план через три дня подтвердится автоматически",
),
"plan_author": MessageLookupByLibrary.simpleMessage("Автор плана"),
"plan_desc": MessageLookupByLibrary.simpleMessage("Описание плана"),
"plan_desc_example": MessageLookupByLibrary.simpleMessage(
@@ -513,6 +519,9 @@ class MessageLookup extends MessageLookupByLibrary {
"plan_seller": MessageLookupByLibrary.simpleMessage("Продавец плана"),
"plan_type": MessageLookupByLibrary.simpleMessage("Тип плана"),
"plans_complete": MessageLookupByLibrary.simpleMessage("Пройденные планы"),
"pleaseConfirmPlan": MessageLookupByLibrary.simpleMessage(
"Просим вас подтвердить прохождение плана",
),
"please_try_again_later": MessageLookupByLibrary.simpleMessage(
"Пожалуйста, попробуйте еще раз позже",
),
@@ -704,6 +713,9 @@ class MessageLookup extends MessageLookupByLibrary {
"wrong_credentials": MessageLookupByLibrary.simpleMessage(
"Неверные данные пользователя",
),
"youCanComplain": MessageLookupByLibrary.simpleMessage(
"Если вас что-то не устроило, вы можете подать жалобу",
),
"youShureYouWantToCancelOrder": MessageLookupByLibrary.simpleMessage(
"Вы уверены, что хотите отменить заказ?",
),

View File

@@ -3184,6 +3184,46 @@ class S {
args: [],
);
}
/// `Просим вас подтвердить прохождение плана`
String get pleaseConfirmPlan {
return Intl.message(
'Просим вас подтвердить прохождение плана',
name: 'pleaseConfirmPlan',
desc: '',
args: [],
);
}
/// `Если вы не подтвердите, то план через три дня подтвердится автоматически`
String get planWillBeConfirmedAfterThreeDays {
return Intl.message(
'Если вы не подтвердите, то план через три дня подтвердится автоматически',
name: 'planWillBeConfirmedAfterThreeDays',
desc: '',
args: [],
);
}
/// `Если вас что-то не устроило, вы можете подать жалобу`
String get youCanComplain {
return Intl.message(
'Если вас что-то не устроило, вы можете подать жалобу',
name: 'youCanComplain',
desc: '',
args: [],
);
}
/// `Подать жалобу`
String get leaveComplainRequest {
return Intl.message(
'Подать жалобу',
name: 'leaveComplainRequest',
desc: '',
args: [],
);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<S> {

View File

@@ -381,6 +381,10 @@
"atLeast3Symbols": "Минимум 3 символа",
"chosePlanType": "Выберите тип плана",
"minPlanPrice": "Минимальная сумма 1200₽",
"addAtLeastOneItem": "Добавьте хотя бы одину опцию"
"addAtLeastOneItem": "Добавьте хотя бы одину опцию",
"pleaseConfirmPlan": "Просим вас подтвердить прохождение плана",
"planWillBeConfirmedAfterThreeDays": "Если вы не подтвердите, то план через три дня подтвердится автоматически",
"youCanComplain": "Если вас что-то не устроило, вы можете подать жалобу",
"leaveComplainRequest": "Подать жалобу"
}

View File

@@ -1,6 +1,6 @@
name: eatvio_app
description: A new Flutter project.
version: 1.0.50+6
version: 1.0.51+6
publish_to: 'none'
environment: