forked from eatvio/eatvio_app
Merge pull request 'feat(chats,diary): added logic for chosen diet portions and added banner to finish plan to chats' (#409) from misha/eatvio_app:main into main
Reviewed-on: eatvio/eatvio_app#409
This commit is contained in:
14
lib/features/chat/models/plan_complain_model.dart
Normal file
14
lib/features/chat/models/plan_complain_model.dart
Normal 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);
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -45,6 +45,7 @@ enum ChosenPlanStateEnum {
|
||||
|
||||
bool isStateAfterPaidState() {
|
||||
switch (this) {
|
||||
case ChosenPlanStateEnum.stateCanceledByTheInitiator:
|
||||
case ChosenPlanStateEnum.stateInited:
|
||||
case ChosenPlanStateEnum.stateInCoordination:
|
||||
case ChosenPlanStateEnum.stateStartDateHasBeenSet:
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user