Compare commits

...

30 Commits

Author SHA1 Message Date
Username1548
96e80ced38 feat(consultantRating): added api requests and state to consultant rating page 2025-06-17 13:33:41 +07:00
mso7nueh
fa1192c5e5 Merge pull request 'upd ver -> 28' (#510) from mso7nueh/eatvio_app:main into main
Reviewed-on: eatvio/eatvio_app#510
2025-06-16 11:33:55 +00:00
mso7nueh
41f948c8f7 upd ver -> 28 2025-06-16 14:33:32 +03:00
mso7nueh
896b2783ae Merge pull request 'feat(consultantFinances), feat(userPurchases)' (#494) from misha/eatvio_app:main into main
Reviewed-on: eatvio/eatvio_app#494
2025-06-16 11:31:05 +00:00
mso7nueh
9c004f8593 Merge pull request 'fix(diary): fixed app review issues' (#502) from misha/eatvio_app:diaryFixes into main
Reviewed-on: eatvio/eatvio_app#502
2025-06-16 11:30:56 +00:00
mso7nueh
a577a8d58a Merge pull request 'fix(chat): time Issue-440' (#509) from mso7nueh/eatvio_app:main into main
Reviewed-on: eatvio/eatvio_app#509
2025-06-16 11:25:29 +00:00
mso7nueh
3b2c4fdff5 fix(chat): time 2025-06-16 13:41:39 +03:00
mso7nueh
44d6487709 Merge pull request 'fix: testflight' (#507) from mso7nueh/eatvio_app:main into main
Reviewed-on: eatvio/eatvio_app#507
2025-06-11 13:35:40 +00:00
mso7nueh
319fd2dfe3 fix: testflight 2025-06-11 16:35:21 +03:00
mso7nueh
a98043758b Merge pull request 'fix: testflight' (#506) from mso7nueh/eatvio_app:main into main
Reviewed-on: eatvio/eatvio_app#506
2025-06-11 13:28:49 +00:00
mso7nueh
a84d2d7daa fix: testflight 2025-06-11 16:28:27 +03:00
Username1548
4acb861b69 merged upstram/main 2025-06-11 20:24:18 +07:00
Username1548
f163608a90 feat(permissions): added galery and camera permisshion checker 2025-06-11 20:13:26 +07:00
6bbec1f022 Merge pull request 'Issue-440 & Issue-194 & build 27' (#503) from mso7nueh/eatvio_app:main into main
Reviewed-on: eatvio/eatvio_app#503
2025-06-11 12:49:09 +00:00
mso7nueh
c0311bf213 feat(chat&notifications): Issue-191 and Issue-440 and auth screen policy 2025-06-11 15:34:31 +03:00
mso7nueh
9d1e621769 feat(chat&notifications): Issue-191 and Issue-440 and auth screen policy 2025-06-11 15:34:16 +03:00
mso7nueh
11b695cefd feat(dev-tools): Issue-441 2025-06-10 17:04:29 +03:00
Username1548
926f0de450 feat(userPurchases, consultantFinances): added status to models and layout for status 2025-06-10 19:57:20 +07:00
mso7nueh
dc81085ae4 fix(chat&reg): fix chat screen, reg email valid 2025-06-09 17:57:10 +03:00
mso7nueh
8c0efc1a34 fix(chat&reg): fix chat screen, reg email valid 2025-06-09 17:57:01 +03:00
Username1548
483cf6d8ee feat(userPurchases): added user purchases, fix(consultations): fixed consultations bug 2025-06-09 14:01:05 +07:00
mso7nueh
87d3f058f6 Merge pull request 'Issue-329' (#500) from mso7nueh/eatvio_app:main into main
Reviewed-on: eatvio/eatvio_app#500
2025-06-06 12:41:37 +00:00
mso7nueh
7a3c6e6b07 Merge remote-tracking branch 'origin/main' 2025-06-06 15:40:37 +03:00
mso7nueh
e6bad9d35d fix(blogPost): Issue-329 2025-06-06 15:40:27 +03:00
mso7nueh
72cf203658 Merge pull request 'build 26 + 1.1.3+33' (#495) from mso7nueh/eatvio_app:main into main
Reviewed-on: eatvio/eatvio_app#495
2025-06-06 08:33:16 +00:00
Username1548
086fd1cac4 Merge remote-tracking branch 'upstream/main' 2025-06-05 22:43:50 +07:00
88e33765d3 Merge pull request 'feat(chats): added refresh timer for chat room, feat(diary): fixed plan duration in plan widget and added date to app bar)' (#492) from misha/eatvio_app:main into main
Reviewed-on: eatvio/eatvio_app#492
2025-06-05 15:43:21 +00:00
Username1548
6fa4bffd7e feat(consultantFinaces): added consultant finaces screen 2025-06-05 22:42:50 +07:00
mso7nueh
70620d8d9e fix(blogPost): update without image 2025-06-05 17:36:49 +03:00
mso7nueh
0b2464ea19 fix(blogPost): update without image 2025-06-05 17:36:15 +03:00
58 changed files with 1678 additions and 785 deletions

View File

@@ -40,6 +40,17 @@ post_install do |installer|
target.build_configurations.each do |config| target.build_configurations.each do |config|
config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64" config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64"
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.0' config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.0'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
## dart: PermissionGroup.camera
'PERMISSION_CAMERA=1',
## dart: PermissionGroup.photos
'PERMISSION_PHOTOS=1',
'PERMISSION_NOTIFICATIONS=1',
## dart: PermissionGroup.mediaLibrary
'PERMISSION_MEDIA_LIBRARY=1',
]
end end
end end
end end

View File

@@ -388,7 +388,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)"; CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 24; CURRENT_PROJECT_VERSION = 27;
DEVELOPMENT_TEAM = VL68C36HYS; DEVELOPMENT_TEAM = VL68C36HYS;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@@ -538,7 +538,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)"; CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 24; CURRENT_PROJECT_VERSION = 27;
DEVELOPMENT_TEAM = VL68C36HYS; DEVELOPMENT_TEAM = VL68C36HYS;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@@ -576,7 +576,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)"; CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 24; CURRENT_PROJECT_VERSION = 27;
DEVELOPMENT_TEAM = VL68C36HYS; DEVELOPMENT_TEAM = VL68C36HYS;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (

View File

@@ -21,7 +21,7 @@
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>24</string> <string>27</string>
<key>ITSAppUsesNonExemptEncryption</key> <key>ITSAppUsesNonExemptEncryption</key>
<false/> <false/>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
@@ -32,11 +32,11 @@
<true/> <true/>
</dict> </dict>
<key>NSCameraUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>Нужно для загрузки фото</string> <string>Предоставление доступа позволит Вам делать фотографии и добавлять к записям</string>
<key>NSLocationWhenInUseUsageDescription</key> <key>NSLocationWhenInUseUsageDescription</key>
<string></string> <string></string>
<key>NSPhotoLibraryUsageDescription</key> <key>NSPhotoLibraryUsageDescription</key>
<string>Нужно для загрузки фото</string> <string>Предоставление доступа позволит Вам добавлять фото к записям</string>
<key>UIApplicationSupportsIndirectInputEvents</key> <key>UIApplicationSupportsIndirectInputEvents</key>
<true/> <true/>
<key>UIBackgroundModes</key> <key>UIBackgroundModes</key>

View File

@@ -1,5 +1,9 @@
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/firebase_api.dart';
import 'package:eatvio_app/core/token_storage.dart';
import 'package:eatvio_app/core/websockets/ws_manager.dart'; import 'package:eatvio_app/core/websockets/ws_manager.dart';
import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/features/auth/presentation/pages/auth_screen.dart'; import 'package:eatvio_app/features/auth/presentation/pages/auth_screen.dart';
@@ -16,6 +20,8 @@ import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.da
import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/become_consultant_screen.dart'; import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/become_consultant_screen.dart';
import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/stepper_become_consultant_screen.dart'; import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/stepper_become_consultant_screen.dart';
import 'package:eatvio_app/features/consultations/presentation/pages/consultations_screen.dart'; import 'package:eatvio_app/features/consultations/presentation/pages/consultations_screen.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/consultant_finances_screen.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/user_purchases_screen.dart';
import 'package:eatvio_app/features/order_info/presentation/info_by_order_screen.dart'; import 'package:eatvio_app/features/order_info/presentation/info_by_order_screen.dart';
import 'package:eatvio_app/features/create_diet/presentation/create_diet_screen.dart'; import 'package:eatvio_app/features/create_diet/presentation/create_diet_screen.dart';
import 'package:eatvio_app/features/create_diet/presentation/diet_preview_screen.dart'; import 'package:eatvio_app/features/create_diet/presentation/diet_preview_screen.dart';
@@ -154,6 +160,23 @@ class AppRouter {
name: 'welcomeScreen', name: 'welcomeScreen',
builder: (context, state) => const WelcomeScreen(), builder: (context, state) => const WelcomeScreen(),
redirect: (context, state) async { redirect: (context, state) async {
try {
if (await TokenStorage.haveTokenInStorage()) {
final firebaseApi = sl<FirebaseApi>();
final fbToken = await firebaseApi.getToken();
final deviceInfo = DeviceInfoPlugin();
await sl<Dio>().post(
'/api/profile/notification/token/register',
data: {
'platform': Platform.isIOS ? 'ios': 'android',
'hw_id': Platform.isIOS ? (await deviceInfo.iosInfo).identifierForVendor : (await deviceInfo.androidInfo).id,
'token': fbToken,
},
);
}
} catch (e) {
rethrow;
}
final sharedPreferences = await SharedPreferences.getInstance(); final sharedPreferences = await SharedPreferences.getInstance();
final welcomeScreenWasShowed = sharedPreferences.getBool('welcomeScreenWasShowed'); final welcomeScreenWasShowed = sharedPreferences.getBool('welcomeScreenWasShowed');
if (welcomeScreenWasShowed == true) { if (welcomeScreenWasShowed == true) {
@@ -181,10 +204,20 @@ class AppRouter {
}, },
), ),
GoRoute( GoRoute(
path: '/consultant-finance-screen', path: ConsultationsArchiveScreen.path,
name: 'consultantFinanceScreen', name: ConsultationsArchiveScreen.name,
builder: (context, state) => const ConsultationsArchiveScreen(), builder: (context, state) => const ConsultationsArchiveScreen(),
), ),
GoRoute(
path: ConsultantFinancesScreen.path,
name: ConsultantFinancesScreen.name,
builder: (context, state) => const ConsultantFinancesScreen(),
),
GoRoute(
path: UserPurchasesScreen.path,
name: UserPurchasesScreen.name,
builder: (context, state) => const UserPurchasesScreen(),
),
GoRoute( GoRoute(
path: '/user-finance-screen', path: '/user-finance-screen',
name: 'userFinanceScreen', name: 'userFinanceScreen',

View File

@@ -1,11 +1,10 @@
import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:firebase_messaging/firebase_messaging.dart';
class FirebaseApi { class FirebaseApi {
final _firebaseMessaging = FirebaseMessaging.instance; final _firebaseMessaging = FirebaseMessaging.instance;
Future<String?> getToken() async{ Future<String?> getToken() async {
await _firebaseMessaging.requestPermission(); await _firebaseMessaging.requestPermission();
return await _firebaseMessaging.getToken(); return await _firebaseMessaging.getToken();
} }
} }

View File

@@ -1,6 +1,7 @@
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/dio/phone_verify_error_interceptor.dart'; import 'package:eatvio_app/core/dio/phone_verify_error_interceptor.dart';
import 'package:eatvio_app/core/firebase_api.dart';
import 'package:eatvio_app/core/interceptors/token_interceptor.dart'; import 'package:eatvio_app/core/interceptors/token_interceptor.dart';
import 'package:eatvio_app/core/websockets/ws_manager.dart'; import 'package:eatvio_app/core/websockets/ws_manager.dart';
import 'package:eatvio_app/core/websockets/ws_service.dart'; import 'package:eatvio_app/core/websockets/ws_service.dart';
@@ -13,6 +14,9 @@ import 'package:eatvio_app/features/cancel_plan_bottom_sheet/service/cancel_plan
import 'package:eatvio_app/features/complaining/presentation/cubit/complaining_cubit.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/repositories/complaining_repository.dart';
import 'package:eatvio_app/features/complaining/services/complaining_service.dart'; import 'package:eatvio_app/features/complaining/services/complaining_service.dart';
import 'package:eatvio_app/features/consultant_rating/presentation/cubit/consultant_rating_cubit.dart';
import 'package:eatvio_app/features/consultant_rating/repositories/consultant_rating_repository.dart';
import 'package:eatvio_app/features/consultant_rating/services/consultant_rating_service.dart';
import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_cubit.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/repositories/consultations_repository.dart';
import 'package:eatvio_app/features/consultations/services/consultation_service.dart'; import 'package:eatvio_app/features/consultations/services/consultation_service.dart';
@@ -30,6 +34,8 @@ import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit
import 'package:eatvio_app/features/blog_post_create/repositories/blog_post_repository.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/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/new_messages_cubit/new_messages_cubit.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_cubit.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_cubit.dart';
import 'package:eatvio_app/features/notifications/presentation/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/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/presentation/bloc/calendar_cubit.dart';
@@ -153,6 +159,9 @@ Future<void> init() async {
sl.registerFactory<VerifyEmailCubit>(() => VerifyEmailCubit(profileRepository: sl())); sl.registerFactory<VerifyEmailCubit>(() => VerifyEmailCubit(profileRepository: sl()));
sl.registerFactory<MyRecipesCubit>(() => MyRecipesCubit(myRecipesRepository: sl(), aboutRecipeRepository: sl())); sl.registerFactory<MyRecipesCubit>(() => MyRecipesCubit(myRecipesRepository: sl(), aboutRecipeRepository: sl()));
sl.registerFactory<ConsultationsArchiveCubit>(() => ConsultationsArchiveCubit(consultationsRepository: sl())); sl.registerFactory<ConsultationsArchiveCubit>(() => ConsultationsArchiveCubit(consultationsRepository: sl()));
sl.registerFactory<ConsultantFinancesCubit>(() => ConsultantFinancesCubit(consultationsRepository: sl()));
sl.registerFactory<UserPurchasesCubit>(() => UserPurchasesCubit(consultationsRepository: sl()));
sl.registerFactory<ConsultantRatingCubit>(() => ConsultantRatingCubit(sl()));
// Repository // Repository
sl.registerLazySingleton<ChatRepository>(() => ChatRepository(chatService: sl())); sl.registerLazySingleton<ChatRepository>(() => ChatRepository(chatService: sl()));
@@ -181,6 +190,7 @@ Future<void> init() async {
sl.registerLazySingleton<CreatePlanRepository>(() => CreatePlanRepository(createPlanService: sl())); sl.registerLazySingleton<CreatePlanRepository>(() => CreatePlanRepository(createPlanService: sl()));
sl.registerLazySingleton<NotificationsRepository>(() => NotificationsRepository(notificationsService: sl())); sl.registerLazySingleton<NotificationsRepository>(() => NotificationsRepository(notificationsService: sl()));
sl.registerFactory<MyRecipesRepository>(() => MyRecipesRepository(myRecipesService: sl())); sl.registerFactory<MyRecipesRepository>(() => MyRecipesRepository(myRecipesService: sl()));
sl.registerFactory<ConsultantRatingRepository>(() => ConsultantRatingRepository(sl()));
// Services // Services
sl.registerLazySingleton<ChatService>(() => ChatService(sl())); sl.registerLazySingleton<ChatService>(() => ChatService(sl()));
@@ -209,7 +219,9 @@ Future<void> init() async {
sl.registerLazySingleton<CancelPlanService>(() => CancelPlanService(sl())); sl.registerLazySingleton<CancelPlanService>(() => CancelPlanService(sl()));
sl.registerLazySingleton<CreatePlanService>(() => CreatePlanService(sl())); sl.registerLazySingleton<CreatePlanService>(() => CreatePlanService(sl()));
sl.registerLazySingleton<NotificationsService>(() => NotificationsService(sl())); sl.registerLazySingleton<NotificationsService>(() => NotificationsService(sl()));
sl.registerLazySingleton<FirebaseApi>(() => FirebaseApi());
sl.registerLazySingleton<MyRecipesService>(() => MyRecipesService(sl())); sl.registerLazySingleton<MyRecipesService>(() => MyRecipesService(sl()));
sl.registerLazySingleton<ConsultantRatingService>(() => ConsultantRatingService(sl()));
// External // External
sl.registerLazySingleton( sl.registerLazySingleton(

View File

@@ -0,0 +1,23 @@
import 'package:eatvio_app/core/widgets/show_error_dialog.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/cupertino.dart';
import 'package:permission_handler/permission_handler.dart';
class PermissionHandler {
static Future<void> checkPermission({
required Permission permission,
required String title,
required String description,
required Future<void> Function() onGranted,
required BuildContext context,
}) async {
final permissionState = await permission.request();
if (permissionState.isGranted) {
await onGranted();
return;
}
if (context.mounted) {
showErrorDialog(context, title: title, description: description, buttonTitle: S.of(context).allowAccess, needToOpenSettings: true);
}
}
}

View File

@@ -8,31 +8,39 @@ extension DateTimeExtension on DateTime {
String pattern = dateTimeFormatPattern, String pattern = dateTimeFormatPattern,
String? locale, String? locale,
]) { ]) {
final localDate = isUtc ? toLocal() : this;
if (locale != null && locale.isNotEmpty) { if (locale != null && locale.isNotEmpty) {
initializeDateFormatting(locale); initializeDateFormatting(locale);
} }
return DateFormat(pattern, locale).format(this); return DateFormat(pattern, locale).format(localDate);
} }
bool isYesterday(DateTime date) { bool isYesterday() {
final DateTime localDate = date.isUtc ? date.toLocal() : date; final localDate = isUtc ? toLocal() : this;
final yesterday = DateTime.now().subtract(const Duration(days: 1));
return yesterday.day == localDate.day && yesterday.month == localDate.month && yesterday.year == localDate.year;
}
bool isToday(DateTime date) {
final DateTime localDate = date.isUtc ? date.toLocal() : date;
final now = DateTime.now(); final now = DateTime.now();
return now.day == localDate.day && now.month == localDate.month && now.year == localDate.year; final yesterday = DateTime(now.year, now.month, now.day).subtract(const Duration(days: 1));
return localDate.year == yesterday.year &&
localDate.month == yesterday.month &&
localDate.day == yesterday.day;
}
bool isToday() {
final localDate = isUtc ? toLocal() : this;
final now = DateTime.now();
return now.year == localDate.year &&
now.month == localDate.month &&
now.day == localDate.day;
} }
} }
String calculateDifference(DateTime dateTime) { String calculateDifference(DateTime dateTime) {
if (dateTime.isToday(dateTime)) { final localDateTime = dateTime.isUtc ? dateTime.toLocal() : dateTime;
if (localDateTime.isToday()) {
return 'Сегодня'; return 'Сегодня';
} else if (dateTime.isYesterday(dateTime)) { } else if (localDateTime.isYesterday()) {
return 'Вчера'; return 'Вчера';
} else { } else {
return dateTime.format('d MMMM', 'ru'); return localDateTime.format('d MMMM', 'ru');
} }
} }

View File

@@ -4,8 +4,9 @@ import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/generated/l10n.dart'; import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:permission_handler/permission_handler.dart';
Future<void> showErrorDialog(BuildContext context, {String? title, String? description}) async { Future<void> showErrorDialog(BuildContext context, {String? title, String? description, String? buttonTitle, bool? needToOpenSettings}) async {
return await showDialog<void>( return await showDialog<void>(
context: context, context: context,
barrierColor: Colors.transparent, barrierColor: Colors.transparent,
@@ -43,8 +44,13 @@ Future<void> showErrorDialog(BuildContext context, {String? title, String? descr
Align( Align(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: TextButton( child: TextButton(
onPressed: () => AppRouter.navigatorKey.currentContext?.pop(), onPressed: () {
child: Text(s.ok, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)), AppRouter.navigatorKey.currentContext?.pop();
if (needToOpenSettings ?? false) {
openAppSettings();
}
},
child: Text(buttonTitle ?? s.ok, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)),
), ),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),

View File

@@ -1,6 +1,4 @@
import 'dart:async'; import 'dart:async';
import 'package:easy_debounce/easy_debounce.dart';
import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart';
@@ -9,8 +7,10 @@ import 'package:eatvio_app/core/utils/toasts.dart';
import 'package:eatvio_app/core/websockets/ws_manager.dart'; import 'package:eatvio_app/core/websockets/ws_manager.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart'; import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/features/auth/presentation/bloc/auth_cubit.dart'; import 'package:eatvio_app/features/auth/presentation/bloc/auth_cubit.dart';
import 'package:eatvio_app/features/auth/presentation/bloc/auth_state.dart';
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart'; import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.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_cubit.dart';
import 'package:eatvio_app/features/dev_tools/presentation/dev_tools_screen.dart';
import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart'; import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart';
import 'package:eatvio_app/generated/l10n.dart'; import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
@@ -23,8 +23,6 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import '../bloc/auth_state.dart';
class AuthScreen extends StatefulWidget { class AuthScreen extends StatefulWidget {
const AuthScreen({super.key}); const AuthScreen({super.key});
@@ -41,6 +39,7 @@ class _AuthScreenState extends State<AuthScreen> {
late StreamSubscription<bool> keyboardSubscription; late StreamSubscription<bool> keyboardSubscription;
bool isFocused = false; bool isFocused = false;
int tapsCount = 0; int tapsCount = 0;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -83,11 +82,8 @@ class _AuthScreenState extends State<AuthScreen> {
tapsCount++; tapsCount++;
if (tapsCount == 6) { if (tapsCount == 6) {
tapsCount = 0; tapsCount = 0;
// showModalBottomSheet(context: context, builder: (context) => );
} }
EasyDebounce.debounce('change env', Durations.medium4, () { GoRouter.of(context).pushNamed(DevToolsScreen.name);
tapsCount = 0;
});
}, },
child: SvgPicture.asset( child: SvgPicture.asset(
ImageConstant.imgLogoEatvio, ImageConstant.imgLogoEatvio,
@@ -153,7 +149,7 @@ class _AuthScreenState extends State<AuthScreen> {
child: RichText( child: RichText(
textAlign: TextAlign.center, textAlign: TextAlign.center,
text: TextSpan( text: TextSpan(
text: 'Нажимая "Войти", Вы соглашаетесь \n', text: 'Нажимая "Войти" или "Зарегистрироваться", Вы соглашаетесь \n',
style: const TextStyle( style: const TextStyle(
fontSize: 12, fontSize: 12,
color: Colors.black, color: Colors.black,
@@ -170,6 +166,17 @@ class _AuthScreenState extends State<AuthScreen> {
), ),
recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse('$baseUrl/oferta_bs.pdf')), recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse('$baseUrl/oferta_bs.pdf')),
), ),
const TextSpan(text: ' и '),
TextSpan(
text: 'Политикой конфиденциальности\nи защитой персональных данных',
style: TextStyle(
fontSize: 12,
color: appTheme.blueA400,
decorationColor: appTheme.blueA400,
decoration: TextDecoration.underline,
),
recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse('$baseUrl/docs/privacy.pdf')),
),
], ],
), ),
), ),

View File

@@ -215,7 +215,10 @@ class _BlogPostScreenState extends State<BlogPostScreen> {
child: Html(data: widget.blogPost.description, style: {"*": Style(color: Colors.black87)}), child: Html(data: widget.blogPost.description, style: {"*": Style(color: Colors.black87)}),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
CachedNetworkImage(imageUrl: '$baseUrl/storage/${widget.blogPost.image}'), if (widget.blogPost.image.isNotEmpty)
CachedNetworkImage(
imageUrl: '$baseUrl/storage/${widget.blogPost.image}',
),
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row( child: Row(

View File

@@ -1,6 +1,8 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
import 'package:eatvio_app/core/theme/app_decoration.dart'; import 'package:eatvio_app/core/theme/app_decoration.dart';
import 'package:eatvio_app/core/widgets/show_error_dialog.dart'; import 'package:eatvio_app/core/widgets/show_error_dialog.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/bloc/blog_post_create_cubit/blog_post_create_cubit.dart';
@@ -17,6 +19,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart';
class BlogPostCreateScreen extends StatefulWidget { class BlogPostCreateScreen extends StatefulWidget {
final FeedBlogPostModel? feedBlogPostModel; final FeedBlogPostModel? feedBlogPostModel;
@@ -80,47 +83,65 @@ class _BlogPostCreateScreenState extends State<BlogPostCreateScreen> {
_showImagePicker() { _showImagePicker() {
showCupertinoModalPopup<void>( showCupertinoModalPopup<void>(
context: context, context: context,
builder: (context) => CupertinoActionSheet( builder: (context) {
actions: [ final s = S.of(context);
CupertinoActionSheetAction( return CupertinoActionSheet(
onPressed: () async { actions: [
final picker = ImagePicker(); CupertinoActionSheetAction(
image = await picker.pickImage(source: ImageSource.camera); onPressed: () async {
setState(() {}); PermissionHandler.checkPermission(
if (!context.mounted) return; permission: Permission.camera,
Navigator.of(context).pop(); title: s.allowAccessToCamera,
}, description: s.allowAccessToCameraDescription,
child: const Row( onGranted: () async {
children: [ final picker = ImagePicker();
Icon(Icons.photo_camera), image = await picker.pickImage(source: ImageSource.camera);
SizedBox(width: 8), setState(() {});
Text('Фото'), AppRouter.navigatorKey.currentContext?.pop();
], },
context: context,
);
},
child: const Row(
children: [
Icon(Icons.photo_camera),
SizedBox(width: 8),
Text('Фото'),
],
),
), ),
), CupertinoActionSheetAction(
CupertinoActionSheetAction( onPressed: () async {
onPressed: () async { PermissionHandler.checkPermission(
final picker = ImagePicker(); permission: Permission.photos,
image = await picker.pickImage(source: ImageSource.gallery); title: s.allowAcessToGalery,
setState(() {}); description: s.allowAcessToGaleryDescription,
if (!context.mounted) return; onGranted: () async {
Navigator.of(context).pop(); final picker = ImagePicker();
}, image = await picker.pickImage(source: ImageSource.gallery);
child: const Row( setState(() {});
children: [ if (!context.mounted) return;
Icon(Icons.photo), AppRouter.navigatorKey.currentContext?.pop();
SizedBox(width: 8), },
Text('Галерея'), context: context,
], );
},
child: const Row(
children: [
Icon(Icons.photo),
SizedBox(width: 8),
Text('Галерея'),
],
),
), ),
],
cancelButton: CupertinoActionSheetAction(
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
), ),
], );
cancelButton: CupertinoActionSheetAction( },
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
),
),
); );
} }

View File

@@ -73,7 +73,7 @@ class BlogPostRepository {
'video_url': videoUrl, 'video_url': videoUrl,
'image': image != null 'image': image != null
? await MultipartFile.fromFile((await ImageCompressUtility.compressFile(File(image.path))).path, filename: image.name) ? await MultipartFile.fromFile((await ImageCompressUtility.compressFile(File(image.path))).path, filename: image.name)
: oldImageUrl != null : oldImageUrl != '' && oldImageUrl != null
? await MultipartFile.fromFile((await downloadImage(oldImageUrl))?.path ?? '', filename: oldImageUrl.split('/').last) ? await MultipartFile.fromFile((await downloadImage(oldImageUrl))?.path ?? '', filename: oldImageUrl.split('/').last)
: null, : null,
}); });

View File

@@ -9,7 +9,6 @@ abstract class ChatUserModel with _$ChatUserModel {
required int id, required int id,
required String name, required String name,
required String slug, required String slug,
@JsonKey(name: 'profile_photo_path') String? profilePhotoPath,
@JsonKey(name: 'profile_photo_url') String? profilePhotoUrl, @JsonKey(name: 'profile_photo_url') String? profilePhotoUrl,
}) = _ChatUserModel; }) = _ChatUserModel;

View File

@@ -2,7 +2,6 @@ import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:easy_debounce/easy_debounce.dart'; import 'package:easy_debounce/easy_debounce.dart';
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/widgets/custom_outlined_button.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/data_error_widget.dart';
import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/core/widgets/loader.dart';
@@ -155,7 +154,7 @@ class _ChatScreenState extends State<ChatScreen> {
if (state.chatRoom.users.receiver.profilePhotoUrl != null) if (state.chatRoom.users.receiver.profilePhotoUrl != null)
CircleAvatar( CircleAvatar(
radius: 20, radius: 20,
foregroundImage: NetworkImage(baseUrl + state.chatRoom.users.receiver.profilePhotoUrl!), foregroundImage: NetworkImage(state.chatRoom.users.receiver.profilePhotoUrl!),
backgroundImage: const AssetImage('assets/images/no_avatar.png'), backgroundImage: const AssetImage('assets/images/no_avatar.png'),
), ),
if (state.chatRoom.users.receiver.profilePhotoUrl == null) if (state.chatRoom.users.receiver.profilePhotoUrl == null)

View File

@@ -1,6 +1,5 @@
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:dismissible_page/dismissible_page.dart'; import 'package:dismissible_page/dismissible_page.dart';
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/theme/custom_text_style.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/theme/theme_helper.dart';
import 'package:eatvio_app/core/utils/date_time_utils.dart'; import 'package:eatvio_app/core/utils/date_time_utils.dart';
@@ -40,7 +39,7 @@ class ChatBubbleWidget extends StatelessWidget {
Padding( Padding(
padding: const EdgeInsets.only(right: 4, bottom: 4), padding: const EdgeInsets.only(right: 4, bottom: 4),
child: Text( child: Text(
sendTime.format('hh:mm'), sendTime.format('HH:mm', 'ru'),
style: const TextStyle( style: const TextStyle(
color: Color(0xFF7B8794), color: Color(0xFF7B8794),
height: 1, height: 1,
@@ -81,7 +80,7 @@ class ChatBubbleWidget extends StatelessWidget {
contentPadding: const EdgeInsets.all(4), contentPadding: const EdgeInsets.all(4),
horizontalTitleGap: 8, horizontalTitleGap: 8,
onTap: () async { onTap: () async {
if (!await launchUrl(Uri.parse('$baseUrl/storage/${mediaData[index]}'))) { if (!await launchUrl(Uri.parse('${mediaData[index]}'))) {
throw Exception('Could not launch ${mediaData[index].toString()}'); throw Exception('Could not launch ${mediaData[index].toString()}');
} }
}, },
@@ -108,18 +107,18 @@ class ChatBubbleWidget extends StatelessWidget {
onTap: () { onTap: () {
context.pushTransparentRoute( context.pushTransparentRoute(
PhotoViewScreen( PhotoViewScreen(
imageUrls: mediaData.map((e) => '$baseUrl/storage/$e').toList(), imageUrls: mediaData.map((e) => '$e').toList(),
index: index, index: index,
), ),
); );
}, },
child: Hero( child: Hero(
tag: '$baseUrl/storage/${mediaData[index]}', tag: '${mediaData[index]}',
transitionOnUserGestures: true, transitionOnUserGestures: true,
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
child: CachedNetworkImage( child: CachedNetworkImage(
imageUrl: '$baseUrl/storage/${mediaData[index]}', imageUrl: '${mediaData[index]}',
), ),
), ),
), ),
@@ -145,7 +144,7 @@ class ChatBubbleWidget extends StatelessWidget {
Padding( Padding(
padding: const EdgeInsets.only(left: 4, bottom: 4), padding: const EdgeInsets.only(left: 4, bottom: 4),
child: Text( child: Text(
sendTime.format('hh:mm'), sendTime.format('HH:mm', 'ru'),
style: const TextStyle( style: const TextStyle(
color: Color(0xFF7B8794), color: Color(0xFF7B8794),
height: 1, height: 1,

View File

@@ -1,4 +1,3 @@
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/utils/date_time_utils.dart'; import 'package:eatvio_app/core/utils/date_time_utils.dart';
import 'package:eatvio_app/features/chat/models/chat_room_pagination_model.dart'; import 'package:eatvio_app/features/chat/models/chat_room_pagination_model.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/custom_text_style.dart';
@@ -42,7 +41,7 @@ class _ChatEntryWidgetState extends State<ChatEntryWidget> {
CircleAvatar( CircleAvatar(
radius: 24, radius: 24,
foregroundImage: NetworkImage( foregroundImage: NetworkImage(
baseUrl + widget.chatRoom.users.toChatUser.profilePhotoUrl, widget.chatRoom.users.toChatUser.profilePhotoUrl,
), ),
backgroundImage: const AssetImage('assets/images/no_avatar.png'), backgroundImage: const AssetImage('assets/images/no_avatar.png'),
backgroundColor: Colors.white, backgroundColor: Colors.white,

View File

@@ -1,16 +1,42 @@
import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
part 'rating_model.freezed.dart'; part 'rating_model.freezed.dart';
part 'rating_model.g.dart'; part 'rating_model.g.dart';
@freezed @freezed
abstract class RatingModel with _$RatingModel { abstract class RatingPaginationModel with _$RatingPaginationModel {
factory RatingPaginationModel({
required List<RatingModel> data,
}) = _RatingPaginationModel;
factory RatingPaginationModel.fromJson(Map<String, dynamic> json) => _$RatingPaginationModelFromJson(json);
}
@freezed
abstract class RatingModel with _$RatingModel {
factory RatingModel({ factory RatingModel({
required DateTime date, @JsonKey(name: 'created_at', fromJson: parseDate) required DateTime date,
required String title, @JsonKey(name: 'log_data') required RatingEnum type,
required int amount, @JsonKey(name: 'log_scores') required int amount,
}) = _RatingModel; }) = _RatingModel;
factory RatingModel.fromJson(Map<String, dynamic> json) => _$RatingModelFromJson(json); factory RatingModel.fromJson(Map<String, dynamic> json) => _$RatingModelFromJson(json);
} }
enum RatingEnum {
@JsonValue('ADDED_RECIPE')
addedRecipe,
@JsonValue('ADDED_PUBLIC_DIET')
addedPublicDiet,
@JsonValue('ADDED_BLOG_POST')
addedBlogPost,
@JsonValue('INVITED_A_NEW_USER')
invitedUser,
@JsonValue('HAS_MEDICAL_DEGREE')
hasMedicalDegree,
@JsonValue('CONSULTED')
consulted,
@JsonValue('PRODUCT_NUTRITINAL_VALUE_APPROVED')
addedGlossary;
}

View File

@@ -0,0 +1,19 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'statistics_model.freezed.dart';
part 'statistics_model.g.dart';
@freezed
abstract class StatisticsModel with _$StatisticsModel {
factory StatisticsModel({
@JsonKey(name: 'count_referal') int? countReferal,
@JsonKey(name: 'count_blog_posts') int? blogPostsCount,
@JsonKey(name: 'count_recipes') int? recipesCount,
@JsonKey(name: 'count_diets') int? dietsCount,
@JsonKey(name: 'count_consultants') int? consultationsCount,
@JsonKey(name: 'count_products_nutritional_values') int? qwe,
}) = _StatisticsModel;
factory StatisticsModel.fromJson(Map<String, dynamic> json) => _$StatisticsModelFromJson(json);
}

View File

@@ -1,12 +1,16 @@
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/custom_text_style.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/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart'; import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
import 'package:eatvio_app/features/consultant_rating/models/rating_model.dart'; import 'package:eatvio_app/core/widgets/data_error_widget.dart';
import 'package:eatvio_app/features/consultant_rating/presentation/cubit/consultant_rating_cubit.dart';
import 'package:eatvio_app/features/consultant_rating/presentation/cubit/consultant_rating_state.dart';
import 'package:eatvio_app/features/consultant_rating/presentation/widgets/rating_history_widget.dart'; import 'package:eatvio_app/features/consultant_rating/presentation/widgets/rating_history_widget.dart';
import 'package:eatvio_app/features/consultant_rating/presentation/widgets/rating_widget.dart'; import 'package:eatvio_app/features/consultant_rating/presentation/widgets/rating_widget.dart';
import 'package:eatvio_app/features/consultant_rating/presentation/widgets/statistic_widget.dart'; import 'package:eatvio_app/features/consultant_rating/presentation/widgets/statistic_widget.dart';
import 'package:eatvio_app/generated/l10n.dart'; import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class ConsultantRatingScreen extends StatelessWidget { class ConsultantRatingScreen extends StatelessWidget {
static const name = 'consultantRatingScreen'; static const name = 'consultantRatingScreen';
@@ -15,7 +19,10 @@ class ConsultantRatingScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return const ConsultantRatingPageContent(); return BlocProvider<ConsultantRatingCubit>(
create: (context) => sl<ConsultantRatingCubit>()..getConsultantStatistics()..getScoreLogs(),
child: const ConsultantRatingPageContent(),
);
} }
} }
@@ -25,52 +32,56 @@ class ConsultantRatingPageContent extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = S.of(context); final s = S.of(context);
return Scaffold( return BlocBuilder<ConsultantRatingCubit, ConsultantRatingState>(
backgroundColor: appTheme.gray100, builder: (context, state) {
appBar: AppBar( return Scaffold(
centerTitle: true, backgroundColor: appTheme.gray100,
title: Text( appBar: AppBar(
s.rating_and_statistics, centerTitle: true,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), title: Text(
), s.rating_and_statistics,
), style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
body: SingleChildScrollView(
child: Column(
children: [
const SizedBox(height: 16),
const RatingWidget(
rating: 1058,
), ),
const SizedBox(height: 16), ),
Padding( body: state.error != null
padding: const EdgeInsets.symmetric(horizontal: 16), ? DataErrorWidget(onReload: () async {
child: CustomOutlinedButton( await context.read<ConsultantRatingCubit>().getConsultantStatistics();
text: s.copy_referral_link, if (!context.mounted) return;
height: 52, await context.read<ConsultantRatingCubit>().getScoreLogs();
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), })
buttonStyle: ButtonStyle( : SingleChildScrollView(
side: const WidgetStatePropertyAll(BorderSide.none), child: Column(
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400), children: [
const SizedBox(height: 16),
const RatingWidget(
rating: 1058,
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: CustomOutlinedButton(
text: s.copy_referral_link,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(
side: const WidgetStatePropertyAll(BorderSide.none),
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
),
onPressed: () {},
),
),
const SizedBox(height: 16),
StatisticWidget(isLoading: state.isStatisticsLoading, statistics: state.statistics,),
const SizedBox(height: 16),
RatingHistoryWidget(
isLoading: state.isRatingLoading,
ratingHistory: state.ratings,
),
],
),
), ),
onPressed: () {}, );
), },
),
const SizedBox(height: 16),
const StatisticWidget(),
const SizedBox(height: 16),
RatingHistoryWidget(
ratingHistory: [
RatingModel(date: DateTime.now(), title: ' djkasjdasjkdkjasjkdjkaskjdjklasjkld', amount: 201300),
RatingModel(date: DateTime.now(), title: 'djkasjdasjkdkjasjkdjkaskjdjklasjkld', amount: 89323),
RatingModel(date: DateTime.now(), title: 'djkasjdasjkdkjasjkdjkaskjdjklasjkld', amount: 20),
RatingModel(date: DateTime.now(), title: 'djkasjdasjkdkjasjkdjkaskjdjklasjkld', amount: 1212),
RatingModel(date: DateTime.now(), title: 'qweqweeq', amount: 323),
RatingModel(date: DateTime.now(), title: 'ee', amount: 1),
],
),
],
),
),
); );
} }
} }

View File

@@ -0,0 +1,32 @@
import 'dart:developer';
import 'package:eatvio_app/features/consultant_rating/presentation/cubit/consultant_rating_state.dart';
import 'package:eatvio_app/features/consultant_rating/repositories/consultant_rating_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class ConsultantRatingCubit extends Cubit<ConsultantRatingState> {
final ConsultantRatingRepository repository;
ConsultantRatingCubit(this.repository) : super(ConsultantRatingState());
Future<void> getConsultantStatistics({String? date}) async {
try {
emit(state.copyWith(isStatisticsLoading: true));
final statistics = await repository.getConsultantStatistics(date: date);
emit(state.copyWith(isStatisticsLoading: false, statistics: statistics, error: null));
} catch (e, st) {
emit(state.copyWith(isStatisticsLoading: true, error: e.toString()));
log(e.toString(), stackTrace: st);
}
}
Future<void> getScoreLogs({String? date}) async {
try {
emit(state.copyWith(isRatingLoading: true));
final scoreLogs = await repository.getScoreLogs(date: date);
emit(state.copyWith(isRatingLoading: false, ratings: scoreLogs, error: null));
} catch (e, st) {
emit(state.copyWith(isRatingLoading: true, error: e.toString()));
log(e.toString(), stackTrace: st);
}
}
}

View File

@@ -0,0 +1,16 @@
import 'package:eatvio_app/features/consultant_rating/models/rating_model.dart';
import 'package:eatvio_app/features/consultant_rating/models/statistics_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'consultant_rating_state.freezed.dart';
@freezed
abstract class ConsultantRatingState with _$ConsultantRatingState {
factory ConsultantRatingState({
@Default(false) bool isStatisticsLoading,
@Default(false) bool isRatingLoading,
@Default(null) StatisticsModel? statistics,
@Default(null) String? error,
@Default(<RatingModel>[]) List<RatingModel> ratings,
}) = _ConsultantRatingState;
}

View File

@@ -1,42 +1,29 @@
import 'package:eatvio_app/core/theme/custom_text_style.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/theme/theme_helper.dart';
import 'package:eatvio_app/core/utils/date_time_utils.dart';
import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart'; import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart';
import 'package:eatvio_app/features/consultant_rating/presentation/cubit/consultant_rating_cubit.dart';
import 'package:eatvio_app/generated/l10n.dart'; import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class PeriodFilterWidget extends StatefulWidget { class PeriodFilterWidget extends StatefulWidget {
const PeriodFilterWidget({super.key}); const PeriodFilterWidget({super.key, required this.isStatistic});
final bool isStatistic;
@override @override
State<PeriodFilterWidget> createState() => _PeriodFilterWidgetState(); State<PeriodFilterWidget> createState() => _PeriodFilterWidgetState();
} }
class _PeriodFilterWidgetState extends State<PeriodFilterWidget> { class _PeriodFilterWidgetState extends State<PeriodFilterWidget> {
final List<String> periodDropDownTitles = []; PeriodFilterStatus _currentState = PeriodFilterStatus.all;
int periodDropDownIndex = 0;
@override
void didChangeDependencies() {
super.didChangeDependencies();
final s = S.of(context);
if (periodDropDownTitles.isEmpty) {
periodDropDownTitles.addAll([
s.for_whole_period_of_time,
s.for_a_year,
s.for_a_month,
s.for_a_week,
]);
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = S.of(context); final s = S.of(context);
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
CustomDropDownButton( CustomDropDownButton<PeriodFilterStatus>(
hint: Text( hint: Text(
s.choose_diet_duration, s.choose_diet_duration,
style: TextStyles.textSMedium.copyWith(color: appTheme.black), style: TextStyles.textSMedium.copyWith(color: appTheme.black),
@@ -44,24 +31,59 @@ class _PeriodFilterWidgetState extends State<PeriodFilterWidget> {
onChanged: (value) { onChanged: (value) {
if (value != null) { if (value != null) {
setState(() { setState(() {
periodDropDownIndex = periodDropDownTitles.indexOf(value); _currentState = value;
}); });
if (widget.isStatistic) {
if (value == PeriodFilterStatus.all) {
context.read<ConsultantRatingCubit>().getConsultantStatistics();
return;
}
context.read<ConsultantRatingCubit>().getConsultantStatistics(date: value.value);
return;
}
if (value == PeriodFilterStatus.all) {
context.read<ConsultantRatingCubit>().getScoreLogs();
return;
}
context.read<ConsultantRatingCubit>().getScoreLogs(date: value.value);
return;
} }
}, },
items: periodDropDownTitles items: PeriodFilterStatus.values
.mapIndexed( .mapIndexed(
(index, value) => DropdownMenuItem( (index, value) => DropdownMenuItem(
value: periodDropDownTitles[index], value: PeriodFilterStatus.values[index],
child: Text( child: Text(
periodDropDownTitles[index], PeriodFilterStatus.values[index].name,
style: TextStyles.textMMedium.copyWith(color: appTheme.black), style: TextStyles.textMMedium.copyWith(color: appTheme.black),
), ),
), ),
) )
.toList(), .toList(),
value: periodDropDownTitles[periodDropDownIndex], value: _currentState,
), ),
], ],
); );
} }
} }
enum PeriodFilterStatus {
all,
month,
week,
year;
String get name => switch (this) {
PeriodFilterStatus.all => 'Все периоды',
PeriodFilterStatus.month => 'За месяц',
PeriodFilterStatus.week => 'За неделю',
PeriodFilterStatus.year => 'За год',
};
String? get value => switch (this) {
PeriodFilterStatus.all => null,
PeriodFilterStatus.month => DateTime.now().subtract(const Duration(days: 30)).format('yyyy-MM-dd'),
PeriodFilterStatus.week => DateTime.now().subtract(const Duration(days: 7)).format('yyyy-MM-dd'),
PeriodFilterStatus.year => DateTime.now().subtract(const Duration(days: 365)).format('yyyy-MM-dd'),
};
}

View File

@@ -11,7 +11,6 @@ class RatingHistoryItem extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
height: 80,
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: appTheme.gray100, color: appTheme.gray100,
@@ -29,7 +28,7 @@ class RatingHistoryItem extends StatelessWidget {
date.format('dd.MM.yyyy'), date.format('dd.MM.yyyy'),
style: TextStyles.textXSSemibold.copyWith(color: appTheme.gray), style: TextStyles.textXSSemibold.copyWith(color: appTheme.gray),
), ),
const Spacer(), const SizedBox(height: 12),
Text( Text(
title, title,
style: TextStyles.textMMedium.copyWith(color: appTheme.black), style: TextStyles.textMMedium.copyWith(color: appTheme.black),

View File

@@ -1,5 +1,6 @@
import 'package:eatvio_app/core/theme/custom_text_style.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/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/core/widgets/main_background_bouble.dart'; import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
import 'package:eatvio_app/features/consultant_rating/models/rating_model.dart'; import 'package:eatvio_app/features/consultant_rating/models/rating_model.dart';
import 'package:eatvio_app/features/consultant_rating/presentation/widgets/period_filter_widget.dart'; import 'package:eatvio_app/features/consultant_rating/presentation/widgets/period_filter_widget.dart';
@@ -9,7 +10,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
class RatingHistoryWidget extends StatelessWidget { class RatingHistoryWidget extends StatelessWidget {
const RatingHistoryWidget({super.key, required this.ratingHistory}); const RatingHistoryWidget({super.key, required this.ratingHistory, required this.isLoading});
final bool isLoading;
final List<RatingModel> ratingHistory; final List<RatingModel> ratingHistory;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -20,50 +22,60 @@ class RatingHistoryWidget extends StatelessWidget {
right: 16, right: 16,
top: 16, top: 16,
), ),
child: Column( child: isLoading
children: [ ? const Center(child: Loader())
Row( : Column(
children: [ children: [
Text( Row(
s.rating_history, children: [
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), Text(
), s.rating_history,
const Spacer(), style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
SvgPicture.asset( ),
'assets/icons/question_mark.svg', const Spacer(),
height: 22, SvgPicture.asset(
width: 22, 'assets/icons/question_mark.svg',
colorFilter: ColorFilter.mode( height: 22,
appTheme.blueA400, width: 22,
BlendMode.srcIn, colorFilter: ColorFilter.mode(
appTheme.blueA400,
BlendMode.srcIn,
),
),
],
), ),
), const SizedBox(
], height: 16,
),
const SizedBox(
height: 16,
),
const PeriodFilterWidget(),
const SizedBox(
height: 12,
),
ListView.builder(
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return Padding(
padding: index == ratingHistory.length - 1 ? EdgeInsets.zero : const EdgeInsets.only(bottom: 12),
child: RatingHistoryItem(
title: ratingHistory[index].title,
amount: ratingHistory[index].amount,
date: ratingHistory[index].date,
), ),
); const PeriodFilterWidget(isStatistic: false),
}, const SizedBox(
itemCount: ratingHistory.length, height: 12,
shrinkWrap: true, ),
), ListView.builder(
], physics: const NeverScrollableScrollPhysics(),
), itemBuilder: (context, index) {
return Padding(
padding: index == ratingHistory.length - 1 ? EdgeInsets.zero : const EdgeInsets.only(bottom: 12),
child: RatingHistoryItem(
title: switch (ratingHistory[index].type) {
RatingEnum.addedRecipe => 'Добавление рецепта',
RatingEnum.addedPublicDiet => 'Добавление публичной диеты',
RatingEnum.addedBlogPost => 'Добавление поста',
RatingEnum.invitedUser => 'Регистрация по реф. ссылке',
RatingEnum.hasMedicalDegree => 'Получение мед. степени',
RatingEnum.consulted => 'Консультация',
RatingEnum.addedGlossary => 'Добавление продукта в глоссарий',
},
amount: ratingHistory[index].amount,
date: ratingHistory[index].date,
),
);
},
itemCount: ratingHistory.length,
shrinkWrap: true,
),
],
),
); );
} }
} }

View File

@@ -1,6 +1,8 @@
import 'package:eatvio_app/core/theme/custom_text_style.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/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/core/widgets/main_background_bouble.dart'; import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
import 'package:eatvio_app/features/consultant_rating/models/statistics_model.dart';
import 'package:eatvio_app/features/consultant_rating/presentation/widgets/period_filter_widget.dart'; import 'package:eatvio_app/features/consultant_rating/presentation/widgets/period_filter_widget.dart';
import 'package:eatvio_app/features/consultant_rating/presentation/widgets/statistic_bouble.dart'; import 'package:eatvio_app/features/consultant_rating/presentation/widgets/statistic_bouble.dart';
import 'package:eatvio_app/generated/l10n.dart'; import 'package:eatvio_app/generated/l10n.dart';
@@ -8,14 +10,15 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
class StatisticWidget extends StatelessWidget { class StatisticWidget extends StatelessWidget {
const StatisticWidget({super.key}); const StatisticWidget({super.key, required this.isLoading, required this.statistics});
final bool isLoading;
final StatisticsModel? statistics;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = S.of(context); final s = S.of(context);
return MainBackgroundBouble( return MainBackgroundBouble(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Column( child: isLoading ? const Center(child: Loader()) : Column(
children: [ children: [
Row( Row(
children: [ children: [
@@ -38,15 +41,17 @@ class StatisticWidget extends StatelessWidget {
const SizedBox( const SizedBox(
height: 16, height: 16,
), ),
const PeriodFilterWidget(), const PeriodFilterWidget(
isStatistic: true,
),
const SizedBox( const SizedBox(
height: 12, height: 12,
), ),
Row( Row(
children: [ children: [
Expanded(child: StatisticBouble(number: 12, title: s.invited_people_count)), Expanded(child: StatisticBouble(number: statistics?.countReferal ?? 0, title: s.invited_people_count)),
const SizedBox(width: 8), const SizedBox(width: 8),
Expanded(child: StatisticBouble(number: 12, title: s.published_posts_count)), Expanded(child: StatisticBouble(number: statistics?.blogPostsCount ?? 0, title: s.published_posts_count)),
], ],
), ),
const SizedBox( const SizedBox(
@@ -54,9 +59,9 @@ class StatisticWidget extends StatelessWidget {
), ),
Row( Row(
children: [ children: [
Expanded(child: StatisticBouble(number: 12, title: s.published_recipes_count)), Expanded(child: StatisticBouble(number: statistics?.recipesCount ?? 0, title: s.published_recipes_count)),
const SizedBox(width: 8), const SizedBox(width: 8),
Expanded(child: StatisticBouble(number: 12, title: s.published_diets_count)), Expanded(child: StatisticBouble(number: statistics?.dietsCount ?? 0, title: s.published_diets_count)),
], ],
), ),
const SizedBox( const SizedBox(
@@ -64,9 +69,9 @@ class StatisticWidget extends StatelessWidget {
), ),
Row( Row(
children: [ children: [
Expanded(child: StatisticBouble(number: 12, title: s.consultations_count)), Expanded(child: StatisticBouble(number: statistics?.consultationsCount ?? 0, title: s.consultations_count)),
const SizedBox(width: 8), const SizedBox(width: 8),
Expanded(child: StatisticBouble(number: 12, title: s.active_consultations_count)), Expanded(child: StatisticBouble(number: statistics?.qwe ?? 0, title: s.active_consultations_count)),
], ],
), ),
const SizedBox( const SizedBox(

View File

@@ -0,0 +1,26 @@
import 'package:eatvio_app/features/consultant_rating/models/rating_model.dart';
import 'package:eatvio_app/features/consultant_rating/models/statistics_model.dart';
import 'package:eatvio_app/features/consultant_rating/services/consultant_rating_service.dart';
class ConsultantRatingRepository {
final ConsultantRatingService service;
ConsultantRatingRepository(this.service);
Future<StatisticsModel> getConsultantStatistics({String? date}) async {
try {
return await service.getConsultantStatistics(date: date);
} catch (e) {
rethrow;
}
}
Future<List<RatingModel>> getScoreLogs({String? date}) async {
try {
final data = (await service.getScoreLogs(date: date)).data;
return data;
} catch (e) {
rethrow;
}
}
}

View File

@@ -0,0 +1,18 @@
import 'package:dio/dio.dart';
import 'package:eatvio_app/features/consultant_rating/models/rating_model.dart';
import 'package:eatvio_app/features/consultant_rating/models/statistics_model.dart';
import 'package:retrofit/retrofit.dart';
part 'consultant_rating_service.g.dart';
@RestApi()
abstract class ConsultantRatingService {
factory ConsultantRatingService(Dio dio, {String baseUrl}) = _ConsultantRatingService;
@GET('/api/consultants/rating/statistics')
Future<StatisticsModel> getConsultantStatistics({@Query('filters[duration][\$gte]') String? date});
@GET('/api/consultants/score/logs')
Future<RatingPaginationModel> getScoreLogs({@Query('filters[duration][\$gte]') String? date});
}

View File

@@ -6,6 +6,7 @@ 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_cubit.dart';
import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_state.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:eatvio_app/features/consultations/presentation/widgets/consultation_card_widget.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/consultations_archive_screen.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
@@ -67,7 +68,7 @@ class ConsultationsContent extends StatelessWidget {
InkWell( InkWell(
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
onTap: () { onTap: () {
GoRouter.of(context).pushNamed('consultantFinanceScreen'); GoRouter.of(context).pushNamed(ConsultationsArchiveScreen.name);
}, },
child: Ink( child: Ink(
width: MediaQuery.of(context).size.width - 32, width: MediaQuery.of(context).size.width - 32,

View File

@@ -185,6 +185,8 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
children: List.generate(widget.consultationsModel.duration, (index) { children: List.generate(widget.consultationsModel.duration, (index) {
final day = givenDaysWithDayNumber.firstWhereOrNull((e) => e.$1 == index); final day = givenDaysWithDayNumber.firstWhereOrNull((e) => e.$1 == index);
final hasActivity = day != null && day.$2.state != DiaryStateEnum.init; final hasActivity = day != null && day.$2.state != DiaryStateEnum.init;
final canConsultantOpenThisDay = day != null &&
(day.$2.state != DiaryStateEnum.init && day.$2.state != DiaryStateEnum.started && day.$2.state != DiaryStateEnum.lastDayStarted);
return Padding( return Padding(
padding: const EdgeInsets.only(right: 12), padding: const EdgeInsets.only(right: 12),
child: Column( child: Column(
@@ -192,7 +194,7 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
children: [ children: [
InkWell( InkWell(
onTap: () { onTap: () {
if (!hasActivity) { if (!hasActivity || !canConsultantOpenThisDay) {
showErrorDialog(context, showErrorDialog(context,
title: s.you_can_not_open_this_day, description: '${widget.consultationsModel.userName} ${s.has_not_filled_it_yet}'); title: s.you_can_not_open_this_day, description: '${widget.consultationsModel.userName} ${s.has_not_filled_it_yet}');
return; return;

View File

@@ -1,7 +1,9 @@
import 'package:eatvio_app/core/enums/filters_comparison_enum.dart'; import 'package:eatvio_app/core/enums/filters_comparison_enum.dart';
import 'package:eatvio_app/features/consultations/models/consultation_model.dart'; import 'package:eatvio_app/features/consultations/models/consultation_model.dart';
import 'package:eatvio_app/features/consultations/services/consultation_service.dart'; import 'package:eatvio_app/features/consultations/services/consultation_service.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/models/consultant_finance_model.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/models/statistic_model.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/models/statistic_model.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/models/user_purchases_model.dart';
class ConsultationsRepository { class ConsultationsRepository {
final ConsultationService consultationService; final ConsultationService consultationService;
@@ -42,4 +44,20 @@ class ConsultationsRepository {
rethrow; rethrow;
} }
} }
Future<List<ConsultantFinanceModel>> getConsultantFinances(String? date, FiltersComparisonEnum? dateComparison) async {
try {
return await consultationService.getConsultantFinances(date, dateComparison);
} catch (e) {
rethrow;
}
}
Future<List<UserPurchasesModel>> getUserPurchases(String? date, FiltersComparisonEnum? dateComparison) async {
try {
return await consultationService.getUserPurchases(date, dateComparison);
} catch (e) {
rethrow;
}
}
} }

View File

@@ -1,7 +1,9 @@
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:eatvio_app/core/enums/filters_comparison_enum.dart'; import 'package:eatvio_app/core/enums/filters_comparison_enum.dart';
import 'package:eatvio_app/features/consultations/models/consultation_model.dart'; import 'package:eatvio_app/features/consultations/models/consultation_model.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/models/consultant_finance_model.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/models/statistic_model.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/models/statistic_model.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/models/user_purchases_model.dart';
class ConsultationService { class ConsultationService {
final Dio dio; final Dio dio;
@@ -66,4 +68,42 @@ class ConsultationService {
rethrow; rethrow;
} }
} }
Future<List<ConsultantFinanceModel>> getConsultantFinances(
String? date,
FiltersComparisonEnum? dateComparison,
) async {
try {
final response = await dio.get(
'/api/finances/my_finances',
queryParameters: {
if (dateComparison != null && date != null) 'filters[end_date]': date,
},
);
final data = response.data as Map<String, dynamic>;
final consultations = data['data'] as List<dynamic>;
return consultations.map((consultation) => ConsultantFinanceModel.fromJson(consultation as Map<String, dynamic>)).toList();
} catch (e) {
rethrow;
}
}
Future<List<UserPurchasesModel>> getUserPurchases(
String? date,
FiltersComparisonEnum? dateComparison,
) async {
try {
final response = await dio.get(
'/api/finances/my_purchases',
queryParameters: {
if (dateComparison != null && date != null) 'filters[start_date]': date,
},
);
final data = response.data as Map<String, dynamic>;
final purchases = data['data'] as List<dynamic>;
return purchases.map((purchase) => UserPurchasesModel.fromJson(purchase as Map<String, dynamic>)).toList();
} catch (e) {
rethrow;
}
}
} }

View File

@@ -1,6 +1,8 @@
import 'dart:io'; import 'dart:io';
import 'package:dotted_border/dotted_border.dart'; import 'package:dotted_border/dotted_border.dart';
import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
import 'package:eatvio_app/core/theme/custom_text_style.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/theme/theme_helper.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart'; import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
@@ -11,7 +13,9 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart';
class DietImagePickerWidget extends StatefulWidget { class DietImagePickerWidget extends StatefulWidget {
const DietImagePickerWidget({super.key}); const DietImagePickerWidget({super.key});
@@ -28,11 +32,18 @@ class _DietImagePickerWidgetState extends State<DietImagePickerWidget> {
actions: [ actions: [
CupertinoActionSheetAction( CupertinoActionSheetAction(
onPressed: () async { onPressed: () async {
final picker = ImagePicker(); PermissionHandler.checkPermission(
final image = await picker.pickImage(source: ImageSource.camera); permission: Permission.camera,
cubit.setImage(image); title: s.allowAccessToCamera,
if (!context.mounted) return; description: s.allowAccessToCameraDescription,
Navigator.of(context).pop(); onGranted: () async {
final picker = ImagePicker();
final image = await picker.pickImage(source: ImageSource.camera);
cubit.setImage(image);
AppRouter.navigatorKey.currentContext?.pop();
},
context: context,
);
}, },
child: Row( child: Row(
children: [ children: [
@@ -44,11 +55,18 @@ class _DietImagePickerWidgetState extends State<DietImagePickerWidget> {
), ),
CupertinoActionSheetAction( CupertinoActionSheetAction(
onPressed: () async { onPressed: () async {
final picker = ImagePicker(); PermissionHandler.checkPermission(
final image = await picker.pickImage(source: ImageSource.gallery); permission: Permission.photos,
cubit.setImage(image); title: s.allowAcessToGalery,
if (!context.mounted) return; description: s.allowAcessToGaleryDescription,
Navigator.of(context).pop(); onGranted: () async {
final picker = ImagePicker();
final image = await picker.pickImage(source: ImageSource.gallery);
cubit.setImage(image);
AppRouter.navigatorKey.currentContext?.pop();
},
context: context,
);
}, },
child: Row( child: Row(
children: [ children: [

View File

@@ -1,6 +1,8 @@
import 'dart:io'; import 'dart:io';
import 'package:dotted_border/dotted_border.dart'; import 'package:dotted_border/dotted_border.dart';
import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
import 'package:eatvio_app/core/theme/custom_text_style.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/theme/theme_helper.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart'; import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
@@ -11,7 +13,9 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart';
class PlanImagePickerWidget extends StatefulWidget { class PlanImagePickerWidget extends StatefulWidget {
const PlanImagePickerWidget({super.key}); const PlanImagePickerWidget({super.key});
@@ -28,11 +32,18 @@ class _PlanImagePickerWidgetState extends State<PlanImagePickerWidget> {
actions: [ actions: [
CupertinoActionSheetAction( CupertinoActionSheetAction(
onPressed: () async { onPressed: () async {
final picker = ImagePicker(); PermissionHandler.checkPermission(
final image = await picker.pickImage(source: ImageSource.camera); permission: Permission.camera,
cubit.setImage(image); title: s.allowAccessToCamera,
if (!context.mounted) return; description: s.allowAccessToCameraDescription,
Navigator.of(context).pop(); onGranted: () async {
final picker = ImagePicker();
final image = await picker.pickImage(source: ImageSource.camera);
cubit.setImage(image);
AppRouter.navigatorKey.currentContext?.pop();
},
context: context,
);
}, },
child: Row( child: Row(
children: [ children: [
@@ -44,11 +55,18 @@ class _PlanImagePickerWidgetState extends State<PlanImagePickerWidget> {
), ),
CupertinoActionSheetAction( CupertinoActionSheetAction(
onPressed: () async { onPressed: () async {
final picker = ImagePicker(); PermissionHandler.checkPermission(
final image = await picker.pickImage(source: ImageSource.gallery); permission: Permission.photos,
cubit.setImage(image); title: s.allowAcessToGalery,
if (!context.mounted) return; description: s.allowAcessToGaleryDescription,
Navigator.of(context).pop(); onGranted: () async {
final picker = ImagePicker();
final image = await picker.pickImage(source: ImageSource.gallery);
cubit.setImage(image);
AppRouter.navigatorKey.currentContext?.pop();
},
context: context,
);
}, },
child: Row( child: Row(
children: [ children: [

View File

@@ -1,11 +1,16 @@
import 'dart:io'; import 'dart:io';
import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart'; import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart';
class ImageWidget extends StatefulWidget { class ImageWidget extends StatefulWidget {
const ImageWidget({super.key, required this.image}); const ImageWidget({super.key, required this.image});
@@ -67,55 +72,72 @@ class _ImageWidgetState extends State<ImageWidget> {
_showImagePicker() { _showImagePicker() {
showCupertinoModalPopup<void>( showCupertinoModalPopup<void>(
context: context, context: context,
builder: (context) => CupertinoActionSheet( builder: (context) {
actions: [ final s = S.of(context);
CupertinoActionSheetAction( return CupertinoActionSheet(
onPressed: () async { actions: [
final picker = ImagePicker(); CupertinoActionSheetAction(
final image = await picker.pickImage(source: ImageSource.camera); onPressed: () async {
setState(() {}); PermissionHandler.checkPermission(
if (image != null) { permission: Permission.camera,
createRecipeCubit.replaceImage(File(image.path)); title: s.allowAccessToCamera,
createRecipeCubit.validateImage(); description: s.allowAccessToCameraDescription,
} onGranted: () async {
if (!context.mounted) return; final picker = ImagePicker();
Navigator.of(context).pop(); final image = await picker.pickImage(source: ImageSource.camera);
}, setState(() {});
child: const Row( if (image != null) {
children: [ createRecipeCubit.replaceImage(File(image.path));
Icon(Icons.photo_camera), createRecipeCubit.validateImage();
SizedBox(width: 8), }
Text('Фото'), AppRouter.navigatorKey.currentContext?.pop();
], },
context: context,
);
},
child: const Row(
children: [
Icon(Icons.photo_camera),
SizedBox(width: 8),
Text('Фото'),
],
),
), ),
), CupertinoActionSheetAction(
CupertinoActionSheetAction( onPressed: () async {
onPressed: () async { PermissionHandler.checkPermission(
final picker = ImagePicker(); permission: Permission.photos,
final image = await picker.pickImage(source: ImageSource.gallery); title: s.allowAcessToGalery,
setState(() {}); description: s.allowAcessToGaleryDescription,
if (image != null) { onGranted: () async {
createRecipeCubit.replaceImage(File(image.path)); final picker = ImagePicker();
createRecipeCubit.validateImage(); final image = await picker.pickImage(source: ImageSource.gallery);
} setState(() {});
if (!context.mounted) return; if (image != null) {
Navigator.of(context).pop(); createRecipeCubit.replaceImage(File(image.path));
}, createRecipeCubit.validateImage();
child: const Row( }
children: [ AppRouter.navigatorKey.currentContext?.pop();
Icon(Icons.photo), },
SizedBox(width: 8), context: context,
Text('Галерея'), );
], },
child: const Row(
children: [
Icon(Icons.photo),
SizedBox(width: 8),
Text('Галерея'),
],
),
), ),
],
cancelButton: CupertinoActionSheetAction(
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
), ),
], );
cancelButton: CupertinoActionSheetAction( },
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
),
),
); );
} }
} }

View File

@@ -1,10 +1,15 @@
import 'dart:io'; import 'dart:io';
import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart'; import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart';
class StepImageWidget extends StatefulWidget { class StepImageWidget extends StatefulWidget {
final CreateRecipeCubit createRecipeCubit; final CreateRecipeCubit createRecipeCubit;
@@ -12,7 +17,8 @@ class StepImageWidget extends StatefulWidget {
const StepImageWidget({ const StepImageWidget({
super.key, super.key,
required this.index, required this.createRecipeCubit, required this.index,
required this.createRecipeCubit,
}); });
@override @override
@@ -79,53 +85,70 @@ class _StepImageWidgetState extends State<StepImageWidget> {
_showImagePicker() { _showImagePicker() {
showCupertinoModalPopup<void>( showCupertinoModalPopup<void>(
context: context, context: context,
builder: (context) => CupertinoActionSheet( builder: (context) {
actions: [ final s = S.of(context);
CupertinoActionSheetAction( return CupertinoActionSheet(
onPressed: () async { actions: [
final picker = ImagePicker(); CupertinoActionSheetAction(
image = await picker.pickImage(source: ImageSource.camera); onPressed: () async {
setState(() {}); PermissionHandler.checkPermission(
if (image != null) { permission: Permission.camera,
widget.createRecipeCubit.replaceImageInStep(File(image!.path), widget.index); title: s.allowAccessToCamera,
} description: s.allowAccessToCameraDescription,
if (!context.mounted) return; onGranted: () async {
Navigator.of(context).pop(); final picker = ImagePicker();
}, image = await picker.pickImage(source: ImageSource.camera);
child: const Row( setState(() {});
children: [ if (image != null) {
Icon(Icons.photo_camera), widget.createRecipeCubit.replaceImageInStep(File(image!.path), widget.index);
SizedBox(width: 8), }
Text('Фото'), AppRouter.navigatorKey.currentContext?.pop();
], },
context: context,
);
},
child: const Row(
children: [
Icon(Icons.photo_camera),
SizedBox(width: 8),
Text('Фото'),
],
),
), ),
), CupertinoActionSheetAction(
CupertinoActionSheetAction( onPressed: () async {
onPressed: () async { PermissionHandler.checkPermission(
final picker = ImagePicker(); permission: Permission.photos,
image = await picker.pickImage(source: ImageSource.gallery); title: s.allowAcessToGalery,
setState(() {}); description: s.allowAcessToGaleryDescription,
if (image != null) { onGranted: () async {
widget.createRecipeCubit.replaceImageInStep(File(image!.path), widget.index); final picker = ImagePicker();
} image = await picker.pickImage(source: ImageSource.gallery);
if (!context.mounted) return; setState(() {});
Navigator.of(context).pop(); if (image != null) {
}, widget.createRecipeCubit.replaceImageInStep(File(image!.path), widget.index);
child: const Row( }
children: [ AppRouter.navigatorKey.currentContext?.pop();
Icon(Icons.photo), },
SizedBox(width: 8), context: context,
Text('Галерея'), );
], },
child: const Row(
children: [
Icon(Icons.photo),
SizedBox(width: 8),
Text('Галерея'),
],
),
), ),
],
cancelButton: CupertinoActionSheetAction(
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
), ),
], );
cancelButton: CupertinoActionSheetAction( },
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
),
),
); );
} }
} }

View File

@@ -1,16 +1,21 @@
import 'dart:io'; import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/diary/models/diary_day_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_cubit.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart';
class PhotoGalleryWidget extends StatefulWidget { class PhotoGalleryWidget extends StatefulWidget {
const PhotoGalleryWidget({super.key}); const PhotoGalleryWidget({super.key});
@@ -32,55 +37,75 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
showImagePicker(DiaryDayModel diaryDay) { showImagePicker(DiaryDayModel diaryDay) {
showCupertinoModalPopup<void>( showCupertinoModalPopup<void>(
context: context, context: context,
builder: (context) => CupertinoActionSheet( builder: (context) {
actions: [ final s = S.of(context);
CupertinoActionSheetAction( return CupertinoActionSheet(
onPressed: () async { actions: [
Navigator.of(context).pop(); CupertinoActionSheetAction(
final picker = ImagePicker(); onPressed: () async {
XFile? image = await picker.pickImage(source: ImageSource.camera); PermissionHandler.checkPermission(
if (image != null) { permission: Permission.camera,
diaryCubit.uploadPhotos( title: s.allowAccessToCamera,
files: [File(image.path)], description: s.allowAccessToCameraDescription,
diaryId: diaryDay.id, onGranted: () async {
AppRouter.navigatorKey.currentContext?.pop();
final picker = ImagePicker();
XFile? image = await picker.pickImage(source: ImageSource.camera);
if (image != null) {
diaryCubit.uploadPhotos(
files: [File(image.path)],
diaryId: diaryDay.id,
);
}
},
context: context,
); );
} },
}, child: const Row(
child: const Row( children: [
children: [ Icon(Icons.photo_camera),
Icon(Icons.photo_camera), SizedBox(width: 8),
SizedBox(width: 8), Text('Фото'),
Text('Фото'), ],
], ),
), ),
), CupertinoActionSheetAction(
CupertinoActionSheetAction( onPressed: () async {
onPressed: () async { PermissionHandler.checkPermission(
Navigator.of(context).pop(); permission: Permission.photos,
final picker = ImagePicker(); title: s.allowAcessToGalery,
List<XFile>? images = await picker.pickMultiImage(); description: s.allowAcessToGaleryDescription,
if (images.isNotEmpty) { onGranted: () async {
diaryCubit.uploadPhotos( AppRouter.navigatorKey.currentContext?.pop();
files: images.map((f) => File(f.path)).toList(), final picker = ImagePicker();
diaryId: diaryDay.id, List<XFile>? images = await picker.pickMultiImage();
if (images.isNotEmpty) {
diaryCubit.uploadPhotos(
files: images.map((f) => File(f.path)).toList(),
diaryId: diaryDay.id,
);
}
},
context: context,
); );
} },
}, child: const Row(
child: const Row( children: [
children: [ Icon(Icons.photo),
Icon(Icons.photo), SizedBox(width: 8),
SizedBox(width: 8), Text('Галерея'),
Text('Галерея'), ],
], ),
), ),
],
cancelButton: CupertinoActionSheetAction(
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
), ),
], );
cancelButton: CupertinoActionSheetAction( },
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
),
),
); );
} }
@@ -92,7 +117,9 @@ class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
int mealIndex = state.selectedMeal - 1; int mealIndex = state.selectedMeal - 1;
if (state.diary.meals[mealIndex].photos.isEmpty && !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented) { if (state.diary.meals[mealIndex].photos.isEmpty && !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented) {
return GestureDetector( return GestureDetector(
onTap: () => showImagePicker(state.diary), onTap: () async {
showImagePicker(state.diary);
},
child: SvgPicture.asset( child: SvgPicture.asset(
width: MediaQuery.of(context).size.width - 32, width: MediaQuery.of(context).size.width - 32,
'assets/no_one_image_was_added.svg', 'assets/no_one_image_was_added.svg',

View File

@@ -20,7 +20,7 @@ abstract class DietsPagination with _$DietsPagination {
abstract class Diet with _$Diet { abstract class Diet with _$Diet {
const factory Diet({ const factory Diet({
@JsonKey(name: "id") required int id, @JsonKey(name: "id") required int id,
@JsonKey(name: "slug") String? slug, @JsonKey(name: "slug") required String slug,
@JsonKey(name: "name") required String name, @JsonKey(name: "name") required String name,
@JsonKey(name: "image") required String image, @JsonKey(name: "image") required String image,
@JsonKey(name: "duration") required int duration, @JsonKey(name: "duration") required int duration,

View File

@@ -1,5 +1,6 @@
import 'dart:developer'; import 'dart:developer';
import 'package:eatvio_app/core/enums/filters_comparison_enum.dart';
import 'package:eatvio_app/features/consultations/repositories/consultations_repository.dart'; import 'package:eatvio_app/features/consultations/repositories/consultations_repository.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_state.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_state.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
@@ -13,19 +14,22 @@ class ConsultantFinancesCubit extends Cubit<ConsultantFinancesState> {
ConsultantFinancesState(), ConsultantFinancesState(),
); );
Future<void> getConsultations() async { Future<void> getConsultantFinances() async {
try { try {
emit(state.copyWith(isLoading: true)); emit(state.copyWith(isLoading: true));
final consultantFinances = await consultationsRepository.getConsultantFinances(
state.periodFilterStatus?.value,
state.periodFilterStatus == PeriodFilterStatus.all ? null : FiltersComparisonEnum.greaterThanOrEqual,
);
emit(state.copyWith(financesModels: consultantFinances, isLoading: false));
} catch (e) { } catch (e) {
emit(state.copyWith(isLoading: false)); emit(state.copyWith(financesModels: [], isLoading: false));
log(e.toString()); log(e.toString());
} }
} }
Future<void> setConsultationsPeriod(PeriodFilterStatus period) async { Future<void> setFinancesPeriod(PeriodFilterStatus period) async {
emit(state.copyWith(periodFilterStatus: period)); emit(state.copyWith(periodFilterStatus: period));
await getConsultations(); await getConsultantFinances();
} }
} }

View File

@@ -1,3 +1,4 @@
import 'package:eatvio_app/features/finances_and_consultations_archive/models/consultant_finance_model.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
@@ -6,7 +7,7 @@ part 'consultant_finances_state.freezed.dart';
@freezed @freezed
abstract class ConsultantFinancesState with _$ConsultantFinancesState { abstract class ConsultantFinancesState with _$ConsultantFinancesState {
factory ConsultantFinancesState({ factory ConsultantFinancesState({
@Default([]) List<String> financesModels, @Default([]) List<ConsultantFinanceModel> financesModels,
@Default(false) isLoading, @Default(false) isLoading,
@Default(null) String? error, @Default(null) String? error,
@Default(null) PeriodFilterStatus? periodFilterStatus, @Default(null) PeriodFilterStatus? periodFilterStatus,

View File

@@ -0,0 +1,36 @@
import 'dart:developer';
import 'package:eatvio_app/core/enums/filters_comparison_enum.dart';
import 'package:eatvio_app/features/consultations/repositories/consultations_repository.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_state.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class UserPurchasesCubit extends Cubit<UserPurchasesState> {
final ConsultationsRepository consultationsRepository;
UserPurchasesCubit({
required this.consultationsRepository,
}) : super(
UserPurchasesState(),
);
Future<void> getUserPurchases() async {
try {
emit(state.copyWith(isLoading: true));
final purchasesModels = await consultationsRepository.getUserPurchases(
state.periodFilterStatus?.value,
state.periodFilterStatus == PeriodFilterStatus.all ? null : FiltersComparisonEnum.greaterThanOrEqual,
);
emit(state.copyWith(purchasesModels: purchasesModels, isLoading: false));
} catch (e) {
emit(state.copyWith(purchasesModels: [], isLoading: false));
log(e.toString());
}
}
Future<void> setPurchasesPeriod(PeriodFilterStatus period) async {
emit(state.copyWith(periodFilterStatus: period));
await getUserPurchases();
}
}

View File

@@ -0,0 +1,15 @@
import 'package:eatvio_app/features/finances_and_consultations_archive/models/user_purchases_model.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'user_purchases_state.freezed.dart';
@freezed
abstract class UserPurchasesState with _$UserPurchasesState {
factory UserPurchasesState({
@Default([]) List<UserPurchasesModel> purchasesModels,
@Default(false) isLoading,
@Default(null) String? error,
@Default(null) PeriodFilterStatus? periodFilterStatus,
}) = _UserPurchasesState;
}

View File

@@ -0,0 +1,33 @@
import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'consultant_finance_model.freezed.dart';
part 'consultant_finance_model.g.dart';
@freezed
abstract class ConsultantFinanceModel with _$ConsultantFinanceModel {
factory ConsultantFinanceModel({
@JsonKey(name: 'start_date', fromJson: parseDate) required DateTime startDate,
@JsonKey(name: 'bank_id') required String bankId,
@JsonKey(name: 'title') required String title,
@JsonKey(name: 'clientName') required String clientName,
@JsonKey(name: 'duration') required int duration,
@JsonKey(name: 'payment_price', fromJson: _parsePayment) required String paymentPrice,
required PaymentStatus? paymentStatus,
}) = _ConsultantFinanceModel;
factory ConsultantFinanceModel.fromJson(Map<String, dynamic> json) => _$ConsultantFinanceModelFromJson(json);
}
String _parsePayment(json) {
return ((json as int) / 100).toStringAsFixed(2);
}
enum PaymentStatus {
@JsonValue('COMPLETED')
completed,
@JsonValue('REJECTED')
rejected,
@JsonValue('CHECKED')
checked
}

View File

@@ -0,0 +1,21 @@
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 'user_purchases_model.freezed.dart';
part 'user_purchases_model.g.dart';
@freezed
abstract class UserPurchasesModel with _$UserPurchasesModel {
factory UserPurchasesModel({
@JsonKey(name: 'start_date', fromJson: parseDate) required DateTime startDate,
@JsonKey(name: 'bank_id') required String bankId,
@JsonKey(name: 'title') required String title,
@JsonKey(name: 'consultantName') required String consultantName,
@JsonKey(name: 'duration') required int duration,
@JsonKey(name: 'price') required int paymentPrice,
@JsonKey(name: 'chosen_plan_last_state') required ChosenPlanStateEnum chosenPlanState,
}) = _UserPurchasesModel;
factory UserPurchasesModel.fromJson(Map<String, dynamic> json) => _$UserPurchasesModelFromJson(json);
}

View File

@@ -1,281 +1,225 @@
// import 'package:eatvio_app/core/locator_service.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/custom_text_style.dart';
// import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart';
// import 'package:eatvio_app/core/utils/date_time_utils.dart'; import 'package:eatvio_app/core/utils/date_time_utils.dart';
// import 'package:eatvio_app/core/utils/localization_utils.dart'; import 'package:eatvio_app/core/utils/localization_utils.dart';
// import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart'; import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart';
// import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/core/widgets/loader.dart';
// import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/archive/consultations_archive_cubit.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_cubit.dart';
// import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/archive/consultations_archive_state.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_state.dart';
// import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_cubit.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/models/consultant_finance_model.dart';
// import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/consultant_finances/consultant_finances_state.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
// import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart';
// import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart'; import 'package:eatvio_app/features/my_finance/presentation/pages/card_settings.dart';
// import 'package:eatvio_app/generated/l10n.dart'; import 'package:eatvio_app/generated/l10n.dart';
// import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
// import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
// class ConsultantFinancesScreen extends StatefulWidget { class ConsultantFinancesScreen extends StatefulWidget {
// const ConsultantFinancesScreen({super.key}); const ConsultantFinancesScreen({super.key});
static const name = 'consultantFinancesScreen';
static const path = '/consultant-finances-screen';
@override
State<ConsultantFinancesScreen> createState() => _ConsultantFinancesScreenState();
}
// @override class _ConsultantFinancesScreenState extends State<ConsultantFinancesScreen> {
// State<ConsultantFinancesScreen> createState() => _ConsultantFinancesScreenState(); @override
// } Widget build(BuildContext context) {
final s = S.of(context);
// class _ConsultantFinancesScreenState extends State<ConsultantFinancesScreen> { return BlocProvider<ConsultantFinancesCubit>(
// @override create: (context) => sl<ConsultantFinancesCubit>()..getConsultantFinances(),
// Widget build(BuildContext context) { child: BlocBuilder<ConsultantFinancesCubit, ConsultantFinancesState>(
// final s = S.of(context); builder: (context, state) {
// return BlocProvider<ConsultantFinancesCubit>( return Scaffold(
// create: (context) => sl<ConsultantFinancesCubit>(), backgroundColor: const Color(0xFFF0F2F5),
// child: BlocBuilder<ConsultantFinancesCubit, ConsultantFinancesState>( appBar: AppBar(
// builder: (context, state) { shape: const RoundedRectangleBorder(
// return Scaffold( borderRadius: BorderRadiusDirectional.vertical(
// backgroundColor: const Color(0xFFF0F2F5), bottom: Radius.circular(12),
// appBar: AppBar( ),
// shape: const RoundedRectangleBorder( ),
// borderRadius: BorderRadiusDirectional.vertical( title: Text(
// bottom: Radius.circular(12), 'Финансы',
// ), style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
// ), ),
// title: const Text('Архив консультаций'), centerTitle: true,
// centerTitle: true, ),
// ), body: state.isLoading
// body: state.isLoading ? const Center(
// ? const Center( child: Loader(),
// child: Loader(), )
// ) : SingleChildScrollView(
// : SingleChildScrollView( padding: const EdgeInsets.symmetric(vertical: 8),
// padding: const EdgeInsets.symmetric(vertical: 8), child: Column(
// child: Column( children: [
// children: [ InkWell(
// Material( onTap: () {
// color: Colors.white, GoRouter.of(context).pushNamed(CardSettingsScreen.name);
// borderRadius: BorderRadius.circular(12), },
// child: Column( borderRadius: BorderRadius.circular(20),
// crossAxisAlignment: CrossAxisAlignment.start, child: Ink(
// children: [ decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), color: appTheme.blueA400),
// const SizedBox(height: 16), width: MediaQuery.of(context).size.width - 32,
// Padding( child: const Center(
// padding: const EdgeInsets.symmetric(horizontal: 16), child: Padding(
// child: Text( padding: EdgeInsets.all(16),
// 'Всего консультаций: ${state.statisticModel.total}', child: Text('Управление картами', style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600)),
// style: const TextStyle( ),
// fontSize: 16, ),
// fontWeight: FontWeight.w500, ),
// color: Color(0xFF6D6F71), ),
// height: 1, const SizedBox(height: 8),
// ), Material(
// ), color: Colors.white,
// ), borderRadius: BorderRadius.circular(12),
// const SizedBox(height: 12), child: Padding(
// Row( padding: const EdgeInsets.only(left: 16, right: 16, top: 16),
// children: [ child: SingleChildScrollView(
// const SizedBox(width: 16), physics: const NeverScrollableScrollPhysics(),
// Material( child: Column(
// color: const Color(0xFF0BB450).withAlpha(11), children: [
// borderRadius: BorderRadius.circular(8), CustomDropDownButton<PeriodFilterStatus>(
// child: Padding( items: PeriodFilterStatus.values
// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), .map(
// child: Text( (value) => DropdownMenuItem(
// 'Завершено: ${state.statisticModel.finished}', value: value,
// style: const TextStyle( child: Text(
// fontSize: 14, value.name,
// fontWeight: FontWeight.w400, style: TextStyles.textMMedium.copyWith(color: appTheme.black),
// color: Color(0xFF0BB450), ),
// height: 1, ),
// ), )
// ), .toList(),
// ), onChanged: (value) {
// ), if (value == null) return;
// const SizedBox(width: 8), context.read<ConsultantFinancesCubit>().setFinancesPeriod(value);
// Material( },
// color: const Color(0xFFD01717).withAlpha(11), value: state.periodFilterStatus ?? PeriodFilterStatus.values.first,
// borderRadius: BorderRadius.circular(8), ),
// child: Padding( const SizedBox(height: 12),
// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), if (state.financesModels.isNotEmpty)
// child: Text( ListView.separated(
// 'Отменено: ${state.statisticModel.canceled}', shrinkWrap: true,
// style: const TextStyle( physics: const NeverScrollableScrollPhysics(),
// fontSize: 14, itemBuilder: (context, index) {
// fontWeight: FontWeight.w400, final Status modelStatus = switch (state.financesModels[index].paymentStatus) {
// color: Color(0xFFD01717), null => Status.inProgress,
// height: 1, PaymentStatus.completed => Status.success,
// ), PaymentStatus.rejected => Status.canceled,
// ), PaymentStatus.checked => Status.success,
// ), };
// ), return Material(
// ], color: const Color(0xFFF6F6F6),
// ), borderRadius: BorderRadius.circular(20),
// const SizedBox(height: 16), child: Padding(
// ], padding: const EdgeInsets.all(12),
// ), child: Column(
// ), crossAxisAlignment: CrossAxisAlignment.start,
// const SizedBox(height: 8), children: [
// Material( Row(
// color: Colors.white, mainAxisAlignment: MainAxisAlignment.spaceBetween,
// borderRadius: BorderRadius.circular(12), children: [
// child: Padding( Builder(builder: (context) {
// padding: const EdgeInsets.only(left: 16, right: 16, top: 16), final idLen = state.financesModels[index].bankId.length;
// child: SingleChildScrollView( return Text(
// physics: const NeverScrollableScrollPhysics(), '${state.financesModels[index].bankId.substring(0, 4)}...${state.financesModels[index].bankId.substring(idLen - 4, idLen)} ',
// child: Column( style: TextStyles.textSMedium.copyWith(color: appTheme.black),
// children: [ );
// CustomDropDownButton<PeriodFilterStatus>( }),
// items: PeriodFilterStatus.values Text(
// .map( state.financesModels[index].startDate.format('yyyy.MM.dd'),
// (value) => DropdownMenuItem( style: const TextStyle(
// value: value, color: Color(0xFF6D6F71),
// child: Text( height: 1,
// value.name, fontWeight: FontWeight.w500,
// style: TextStyles.textMMedium.copyWith(color: appTheme.black), ),
// ), ),
// ), ],
// ) ),
// .toList(), const SizedBox(height: 8),
// onChanged: (value) { Text(
// if (value == null) return; state.financesModels[index].title,
// context.read<ConsultationsArchiveCubit>().setConsultationsPeriod(value); style: TextStyle(
// }, color: appTheme.blueA400,
// value: state.periodFilterStatus ?? PeriodFilterStatus.values.first, height: 1,
// ), fontSize: 18,
// const SizedBox(height: 12), fontWeight: FontWeight.w600,
// CustomDropDownButton<ConsultationFilterStatus>( ),
// items: ConsultationFilterStatus.values ),
// .map( const SizedBox(height: 8),
// (value) => DropdownMenuItem( Text(
// value: value, 'Покупатель: ${state.financesModels[index].clientName}',
// child: Text( style: const TextStyle(
// value.name, color: Color(0xFF323F4B),
// style: TextStyles.textMMedium.copyWith(color: appTheme.black), height: 1,
// ), fontSize: 14,
// ), fontWeight: FontWeight.w500,
// ) ),
// .toList(), ),
// onChanged: (value) { const SizedBox(height: 8),
// if (value == null) return; Text(
// context.read<ConsultationsArchiveCubit>().setConsultationsStatus(value); '${state.financesModels[index].duration} ${getDayForm(state.financesModels[index].duration, s)}',
// }, style: const TextStyle(
// value: state.consultationFilterStatus, color: Color(0xFF323F4B),
// ), height: 1,
// const SizedBox(height: 12), fontSize: 14,
// if (state.consultations.isNotEmpty) fontWeight: FontWeight.w500,
// ListView.separated( ),
// shrinkWrap: true, ),
// physics: const NeverScrollableScrollPhysics(), const SizedBox(height: 8),
// itemBuilder: (context, index) { Container(
// return Material( height: 1,
// color: const Color(0xFFF6F6F6), width: double.infinity,
// borderRadius: BorderRadius.circular(20), decoration: const BoxDecoration(
// child: Padding( color: Color(0xFFDEDEDF),
// padding: const EdgeInsets.all(12), ),
// child: Column( ),
// crossAxisAlignment: CrossAxisAlignment.start, const SizedBox(height: 8),
// children: [ Row(
// Row( mainAxisAlignment: MainAxisAlignment.spaceBetween,
// mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
// children: [ Text(
// Text( '${state.financesModels[index].paymentPrice} ₽',
// state.consultations[index].id.toString(), style: const TextStyle(
// style: const TextStyle( color: Color(0xFF323F4B),
// color: Color(0xFF323F4B), height: 1,
// height: 1, fontSize: 18,
// fontWeight: FontWeight.w500, fontWeight: FontWeight.w600,
// ), ),
// ), ),
// Text( StatusWidget(
// state.consultations[index].startDate.format('yyyy.MM.dd'), status: modelStatus,
// style: const TextStyle( ),
// color: Color(0xFF6D6F71), ],
// height: 1, ),
// fontWeight: FontWeight.w500, ],
// ), ),
// ), ),
// ], );
// ), },
// const SizedBox(height: 8), separatorBuilder: (context, index) => const SizedBox(height: 12),
// Text( itemCount: state.financesModels.length,
// state.consultations[index].planTitle, )
// style: TextStyle( else
// color: appTheme.blueA400, const Padding(
// height: 1, padding: EdgeInsets.symmetric(vertical: 40.0),
// fontSize: 18, child: Text(
// fontWeight: FontWeight.w600, 'У вас нет таких консультаций',
// ), style: TextStyles.textMMedium,
// ), ),
// const SizedBox(height: 8), ),
// Text( ],
// 'Покупатель: ${state.consultations[index].userName}', ),
// style: const TextStyle( ),
// color: Color(0xFF323F4B), ),
// height: 1, ),
// fontSize: 14, ],
// fontWeight: FontWeight.w500, )),
// ), );
// ), },
// const SizedBox(height: 8), ),
// Text( );
// '${state.consultations[index].duration} ${getDayForm(state.consultations[index].duration, s)}', }
// style: const TextStyle( }
// color: Color(0xFF323F4B),
// height: 1,
// fontSize: 14,
// fontWeight: FontWeight.w500,
// ),
// ),
// const SizedBox(height: 8),
// Container(
// height: 1,
// width: double.infinity,
// decoration: const BoxDecoration(
// color: Color(0xFFDEDEDF),
// ),
// ),
// const SizedBox(height: 8),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Text(
// '${state.consultations[index].consultantPayment.toStringAsFixed(2)} ₽',
// style: const TextStyle(
// color: Color(0xFF323F4B),
// height: 1,
// fontSize: 18,
// fontWeight: FontWeight.w600,
// ),
// ),
// StatusWidget(
// status: state.consultations[index].chosenPlanState,
// ),
// ],
// ),
// ],
// ),
// ),
// );
// },
// separatorBuilder: (context, index) => const SizedBox(height: 12),
// itemCount: state.financesModels.length,
// )
// else
// const Padding(
// padding: EdgeInsets.symmetric(vertical: 40.0),
// child: Text(
// 'У вас нет таких консультаций',
// style: TextStyles.textMMedium,
// ),
// ),
// ],
// ),
// ),
// ),
// ),
// ],
// ),
// ),
// );
// },
// ),
// );
// }
// }

View File

@@ -9,13 +9,15 @@ import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/arc
import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/archive/consultations_archive_state.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/archive/consultations_archive_state.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart';
import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
import 'package:eatvio_app/generated/l10n.dart'; import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
class ConsultationsArchiveScreen extends StatefulWidget { class ConsultationsArchiveScreen extends StatefulWidget {
const ConsultationsArchiveScreen({super.key}); const ConsultationsArchiveScreen({super.key});
static const name = 'consultationsArchiveScreen';
static const path = '/consultations-archive-screen';
@override @override
State<ConsultationsArchiveScreen> createState() => _ConsultationsArchiveScreenState(); State<ConsultationsArchiveScreen> createState() => _ConsultationsArchiveScreenState();
} }
@@ -164,6 +166,15 @@ class _ConsultationsArchiveScreenState extends State<ConsultationsArchiveScreen>
shrinkWrap: true, shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) { itemBuilder: (context, index) {
final Status consultationStatus;
if (state.consultations[index].chosenPlanState == ChosenPlanStateEnum.stateCanceledByTheInitiator) {
consultationStatus = Status.canceled;
} else if (state.consultations[index].chosenPlanState == ChosenPlanStateEnum.stateChargeWasCollected) {
consultationStatus = Status.success;
} else {
consultationStatus = Status.inProgress;
}
return Material( return Material(
color: const Color(0xFFF6F6F6), color: const Color(0xFFF6F6F6),
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
@@ -245,7 +256,7 @@ class _ConsultationsArchiveScreenState extends State<ConsultationsArchiveScreen>
), ),
), ),
StatusWidget( StatusWidget(
status: state.consultations[index].chosenPlanState, status: consultationStatus,
), ),
], ],
), ),

View File

@@ -0,0 +1,209 @@
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/utils/date_time_utils.dart';
import 'package:eatvio_app/core/utils/localization_utils.dart';
import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_cubit.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/cubit/user_purchases/user_purchases_state.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/period_enum.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/widgets/status_widget.dart';
import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class UserPurchasesScreen extends StatefulWidget {
const UserPurchasesScreen({super.key});
static const name = 'userPurchasesScreen';
static const path = '/user-purchases-screen';
@override
State<UserPurchasesScreen> createState() => _UserPurchasesScreen();
}
class _UserPurchasesScreen extends State<UserPurchasesScreen> {
@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocProvider<UserPurchasesCubit>(
create: (context) => sl<UserPurchasesCubit>()..getUserPurchases(),
child: BlocBuilder<UserPurchasesCubit, UserPurchasesState>(
builder: (context, state) {
return Scaffold(
backgroundColor: const Color(0xFFF0F2F5),
appBar: AppBar(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadiusDirectional.vertical(
bottom: Radius.circular(12),
),
),
title: Text(
'Финансы',
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
centerTitle: true,
),
body: state.isLoading
? const Center(
child: Loader(),
)
: SingleChildScrollView(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Column(
children: [
const SizedBox(height: 8),
Material(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.only(left: 16, right: 16, top: 16),
child: SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(),
child: Column(
children: [
CustomDropDownButton<PeriodFilterStatus>(
items: PeriodFilterStatus.values
.map(
(value) => DropdownMenuItem(
value: value,
child: Text(
value.name,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
),
)
.toList(),
onChanged: (value) {
if (value == null) return;
context.read<UserPurchasesCubit>().setPurchasesPeriod(value);
},
value: state.periodFilterStatus ?? PeriodFilterStatus.values.first,
),
const SizedBox(height: 12),
if (state.purchasesModels.isNotEmpty)
ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
final Status purchaseStatus;
if (state.purchasesModels[index].chosenPlanState == ChosenPlanStateEnum.stateCanceledByTheInitiator) {
purchaseStatus = Status.canceled;
} else if (state.purchasesModels[index].chosenPlanState == ChosenPlanStateEnum.stateChargeWasCollected) {
purchaseStatus = Status.success;
} else {
purchaseStatus = Status.inProgress;
}
return Material(
color: const Color(0xFFF6F6F6),
borderRadius: BorderRadius.circular(20),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Builder(builder: (context) {
final idLen = state.purchasesModels[index].bankId.length;
return Text(
'${state.purchasesModels[index].bankId.substring(0, 4)}...${state.purchasesModels[index].bankId.substring(idLen - 4, idLen)} ',
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
);
}),
Text(
state.purchasesModels[index].startDate.format('yyyy.MM.dd'),
style: const TextStyle(
color: Color(0xFF6D6F71),
height: 1,
fontWeight: FontWeight.w500,
),
),
],
),
const SizedBox(height: 8),
Text(
state.purchasesModels[index].title,
style: TextStyle(
color: appTheme.blueA400,
height: 1,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 8),
Text(
'Продавец: ${state.purchasesModels[index].consultantName}',
style: const TextStyle(
color: Color(0xFF323F4B),
height: 1,
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 8),
Text(
'${state.purchasesModels[index].duration} ${getDayForm(state.purchasesModels[index].duration, s)}',
style: const TextStyle(
color: Color(0xFF323F4B),
height: 1,
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 8),
Container(
height: 1,
width: double.infinity,
decoration: const BoxDecoration(
color: Color(0xFFDEDEDF),
),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${state.purchasesModels[index].paymentPrice} ₽',
style: const TextStyle(
color: Color(0xFF323F4B),
height: 1,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
StatusWidget(
status: purchaseStatus,
),
],
),
],
),
),
);
},
separatorBuilder: (context, index) => const SizedBox(height: 12),
itemCount: state.purchasesModels.length,
)
else
const Padding(
padding: EdgeInsets.symmetric(vertical: 40.0),
child: Text(
'У вас нет таких консультаций',
style: TextStyles.textMMedium,
),
),
],
),
),
),
),
],
)),
);
},
),
);
}
}

View File

@@ -1,15 +1,15 @@
import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class StatusWidget extends StatelessWidget { class StatusWidget extends StatelessWidget {
const StatusWidget({super.key, required this.status}); const StatusWidget({super.key, required this.status});
final ChosenPlanStateEnum status; final Status status;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (status == ChosenPlanStateEnum.stateCanceledByTheInitiator) { return switch (status){
return Material(
Status.canceled => Material(
color: appTheme.lightRed, color: appTheme.lightRed,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
child: Padding( child: Padding(
@@ -24,9 +24,9 @@ class StatusWidget extends StatelessWidget {
), ),
), ),
), ),
); ),
} else if (status == ChosenPlanStateEnum.stateChargeWasCollected) {
return Material( Status.success => Material(
color: const Color(0xFF0BB450).withAlpha(11), color: const Color(0xFF0BB450).withAlpha(11),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
child: const Padding( child: const Padding(
@@ -41,9 +41,9 @@ class StatusWidget extends StatelessWidget {
), ),
), ),
), ),
); ),
}
return Material( Status.inProgress => Material(
color: appTheme.transparentBlue, color: appTheme.transparentBlue,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
child: Padding( child: Padding(
@@ -58,6 +58,13 @@ class StatusWidget extends StatelessWidget {
), ),
), ),
), ),
); ),
};
} }
} }
enum Status {
canceled,
success,
inProgress
}

View File

@@ -4,6 +4,9 @@ import 'package:eatvio_app/core/token_storage.dart';
import 'package:eatvio_app/core/utils/image_constant.dart'; import 'package:eatvio_app/core/utils/image_constant.dart';
import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/consultant_rating/presentation/consultant_rating_screen.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/consultant_finances_screen.dart';
import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/user_purchases_screen.dart';
import 'package:eatvio_app/features/my_diets/presentation/my_diets_screen.dart'; import 'package:eatvio_app/features/my_diets/presentation/my_diets_screen.dart';
import 'package:eatvio_app/features/my_plans/presentation/my_plans_screen.dart'; import 'package:eatvio_app/features/my_plans/presentation/my_plans_screen.dart';
import 'package:eatvio_app/features/profile/models/profile_model.dart'; import 'package:eatvio_app/features/profile/models/profile_model.dart';
@@ -40,9 +43,22 @@ class MenuScreen extends StatelessWidget {
leading: 'assets/icons/finance.svg', leading: 'assets/icons/finance.svg',
title: 'Мои финансы', title: 'Мои финансы',
onTap: () { onTap: () {
GoRouter.of(context).pushNamed('userFinanceScreen'); if (profileModel.roles.contains('consultant')) {
GoRouter.of(context).push(ConsultantFinancesScreen.path);
return;
}
GoRouter.of(context).push(UserPurchasesScreen.path);
}, },
), ),
if (profileModel.roles.contains('consultant')) _divider(context),
if (profileModel.roles.contains('consultant'))
_tile(
leading: 'assets/icons/finance.svg',
title: 'Мой рейтинг',
onTap: () {
GoRouter.of(context).push(ConsultantRatingScreen.path);
},
),
_divider(context), _divider(context),
_tile(leading: ImageConstant.icnGlossary, title: 'Глоссарий', onTap: () => GoRouter.of(context).push('/glossary-screen')), _tile(leading: ImageConstant.icnGlossary, title: 'Глоссарий', onTap: () => GoRouter.of(context).push('/glossary-screen')),
_divider(context), _divider(context),

View File

@@ -2,8 +2,10 @@ import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:dismissible_page/dismissible_page.dart'; import 'package:dismissible_page/dismissible_page.dart';
import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/enums/complained_content.dart'; import 'package:eatvio_app/core/enums/complained_content.dart';
import 'package:eatvio_app/core/permission_handler/permission_handler.dart';
import 'package:eatvio_app/core/utils/date_time_utils.dart'; import 'package:eatvio_app/core/utils/date_time_utils.dart';
import 'package:eatvio_app/core/widgets/data_error_widget.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/loader.dart';
@@ -26,6 +28,7 @@ import 'package:eatvio_app/features/profile/presentation/widgets/profile_blog_po
import 'package:eatvio_app/features/profile/presentation/widgets/weight_chart_widget.dart'; import 'package:eatvio_app/features/profile/presentation/widgets/weight_chart_widget.dart';
import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
@@ -34,6 +37,7 @@ import 'package:go_router/go_router.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
@@ -440,12 +444,12 @@ class _ProfileScreenState extends State<ProfileScreen> {
padding: const EdgeInsets.only(bottom: 16), padding: const EdgeInsets.only(bottom: 16),
child: Column( child: Column(
children: [ children: [
const Padding( Padding(
padding: EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( const Text(
'Дипломы\nи сертификаты', 'Дипломы\nи сертификаты',
style: TextStyle( style: TextStyle(
color: Color(0xFF323F4B), color: Color(0xFF323F4B),
@@ -454,19 +458,19 @@ class _ProfileScreenState extends State<ProfileScreen> {
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),
// GestureDetector( GestureDetector(
// onTap: () { onTap: () {
// GoRouter.of(context).pushNamed('stepperBecomeConsultantScreen', extra: state.profile.slug); GoRouter.of(context).pushNamed('stepperBecomeConsultantScreen', extra: state.profile.slug);
// }, },
// child: Text( child: Text(
// widget.author == null || widget.author!.authorIsMe ? 'Редактировать' : '', widget.author == null || widget.author!.authorIsMe ? 'Редактировать' : '',
// style: const TextStyle( style: const TextStyle(
// color: Color(0xFF2F80ED), color: Color(0xFF2F80ED),
// fontSize: 16, fontSize: 16,
// fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
// ), ),
// ), ),
// ), ),
], ],
), ),
), ),
@@ -541,12 +545,12 @@ class _ProfileScreenState extends State<ProfileScreen> {
padding: const EdgeInsets.only(bottom: 16), padding: const EdgeInsets.only(bottom: 16),
child: Column( child: Column(
children: [ children: [
const Padding( Padding(
padding: EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( const Text(
'Образование', 'Образование',
style: TextStyle( style: TextStyle(
color: Color(0xFF323F4B), color: Color(0xFF323F4B),
@@ -554,19 +558,19 @@ class _ProfileScreenState extends State<ProfileScreen> {
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),
// GestureDetector( GestureDetector(
// onTap: () { onTap: () {
// GoRouter.of(context).pushNamed('stepperBecomeConsultantScreen', extra: state.profile.slug); GoRouter.of(context).pushNamed('stepperBecomeConsultantScreen', extra: state.profile.slug);
// }, },
// child: Text( child: Text(
// widget.author == null || widget.author!.authorIsMe ? 'Редактировать' : '', widget.author == null || widget.author!.authorIsMe ? 'Редактировать' : '',
// style: const TextStyle( style: const TextStyle(
// color: Color(0xFF2F80ED), color: Color(0xFF2F80ED),
// fontSize: 16, fontSize: 16,
// fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
// ), ),
// ), ),
// ), ),
], ],
), ),
), ),
@@ -600,9 +604,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
_educationBlock(String title, String date) { _educationBlock(String title, String date) {
final parsedDate = DateTime.tryParse(date); final parsedDate = DateTime.tryParse(date);
final formattedDate = parsedDate != null final formattedDate = parsedDate != null ? DateFormat('d MMMM y', 'ru_RU').format(parsedDate) : date;
? DateFormat('d MMMM y', 'ru_RU').format(parsedDate)
: date;
return SizedBox( return SizedBox(
width: double.infinity, width: double.infinity,
@@ -640,7 +642,6 @@ class _ProfileScreenState extends State<ProfileScreen> {
); );
} }
_socialLinks(ProfileStateLoaded state) { _socialLinks(ProfileStateLoaded state) {
if (!state.profile.roles.contains('consultant')) { if (!state.profile.roles.contains('consultant')) {
return const SizedBox.shrink(); return const SizedBox.shrink();
@@ -993,51 +994,70 @@ class _ProfileScreenState extends State<ProfileScreen> {
if (value == 'replace') { if (value == 'replace') {
showCupertinoModalPopup<void>( showCupertinoModalPopup<void>(
context: context, context: context,
builder: (context) => CupertinoActionSheet( builder: (context) {
actions: [ final s = S.of(context);
CupertinoActionSheetAction( return CupertinoActionSheet(
onPressed: () async { actions: [
Navigator.of(context).pop(); CupertinoActionSheetAction(
final picker = ImagePicker(); onPressed: () async {
XFile? image = await picker.pickImage(source: ImageSource.camera); PermissionHandler.checkPermission(
if (image != null) { permission: Permission.camera,
await profileCubit.uploadPhoto(File(image.path)); title: s.allowAccessToCamera,
await sl<ProfileAvatarCubit>().getMyProfile(); description: s.allowAccessToCameraDescription,
} onGranted: () async {
}, AppRouter.navigatorKey.currentContext?.pop();
child: const Row( final picker = ImagePicker();
children: [ XFile? image = await picker.pickImage(source: ImageSource.camera);
Icon(Icons.photo_camera), if (image != null) {
SizedBox(width: 8), await profileCubit.uploadPhoto(File(image.path));
Text('Фото'), await sl<ProfileAvatarCubit>().getMyProfile();
], }
},
context: context,
);
},
child: const Row(
children: [
Icon(Icons.photo_camera),
SizedBox(width: 8),
Text('Фото'),
],
),
), ),
), CupertinoActionSheetAction(
CupertinoActionSheetAction( onPressed: () async {
onPressed: () async { PermissionHandler.checkPermission(
Navigator.of(context).pop(); permission: Permission.photos,
final picker = ImagePicker(); title: s.allowAcessToGalery,
XFile? image = await picker.pickImage(source: ImageSource.gallery); description: s.allowAcessToGaleryDescription,
if (image != null) { onGranted: () async {
await profileCubit.uploadPhoto(File(image.path)); AppRouter.navigatorKey.currentContext?.pop();
await sl<ProfileAvatarCubit>().getMyProfile(); final picker = ImagePicker();
} XFile? image = await picker.pickImage(source: ImageSource.gallery);
}, if (image != null) {
child: const Row( await profileCubit.uploadPhoto(File(image.path));
children: [ await sl<ProfileAvatarCubit>().getMyProfile();
Icon(Icons.photo), }
SizedBox(width: 8), },
Text('Галерея'), context: context,
], );
},
child: const Row(
children: [
Icon(Icons.photo),
SizedBox(width: 8),
Text('Галерея'),
],
),
), ),
],
cancelButton: CupertinoActionSheetAction(
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
), ),
], );
cancelButton: CupertinoActionSheetAction( },
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
),
),
); );
} }
if (value == 'delete') { if (value == 'delete') {

View File

@@ -1,12 +1,13 @@
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:dismissible_page/dismissible_page.dart'; import 'package:dismissible_page/dismissible_page.dart';
import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/enums/complained_content.dart';
import 'package:eatvio_app/core/utils/date_time_utils.dart'; import 'package:eatvio_app/core/utils/date_time_utils.dart';
import 'package:eatvio_app/core/widgets/custom_popup.dart';
import 'package:eatvio_app/core/widgets/feed_icon_button.dart'; import 'package:eatvio_app/core/widgets/feed_icon_button.dart';
import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.dart'; import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/subscribe_button_widget.dart';
import 'package:eatvio_app/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart'; import 'package:eatvio_app/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart';
import 'package:eatvio_app/features/profile/presentation/bloc/profile_cubit/profile_state.dart'; import 'package:eatvio_app/features/profile/presentation/bloc/profile_cubit/profile_state.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -93,26 +94,33 @@ class _ProfileBlogPostWidgetState extends State<ProfileBlogPostWidget> {
], ],
), ),
const Spacer(), const Spacer(),
if (!state.blogPosts[widget.index].author.isEntitySubscribedByMe! && !state.blogPosts[widget.index].author.authorIsMe) CustomPopup(
SubscribeButtonWidget( isAuthor: state.blogPosts[widget.index].author.authorIsMe,
slug: state.blogPosts[widget.index].author.authorSlug, complainedContentType: ComplainedContent.posts,
), path: 'blog_post/${(state.blogPosts[widget.index].id)}',
id: state.blogPosts[widget.index].id,
feedBlogPostModel: state.blogPosts[widget.index],
),
], ],
), ),
), ),
if (state.blogPosts[widget.index].title.isNotEmpty || state.blogPosts[widget.index].description.isNotEmpty) const SizedBox(height: 12), if (state.blogPosts[widget.index].title.isNotEmpty) ...[
Padding( const SizedBox(height: 12),
padding: const EdgeInsets.symmetric(horizontal: 12), Padding(
child: Text( padding: const EdgeInsets.symmetric(horizontal: 12),
state.blogPosts[widget.index].title, child: Text(
style: const TextStyle( state.blogPosts[widget.index].title,
fontWeight: FontWeight.w600, style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.w600,
color: Colors.black87, fontSize: 18,
color: Colors.black87,
),
), ),
), ),
), ],
if (state.blogPosts[widget.index].title.isNotEmpty && state.blogPosts[widget.index].description.isNotEmpty) const SizedBox(height: 8), if (state.blogPosts[widget.index].title.isEmpty) const SizedBox(height: 8),
if (state.blogPosts[widget.index].title.isNotEmpty && state.blogPosts[widget.index].description.isNotEmpty)
const SizedBox(height: 8),
if (state.blogPosts[widget.index].description.isNotEmpty) if (state.blogPosts[widget.index].description.isNotEmpty)
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 4), padding: const EdgeInsets.symmetric(horizontal: 4),

View File

@@ -3,6 +3,7 @@ import 'dart:developer';
import 'package:eatvio_app/core/app_router.dart'; import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/token_storage.dart'; import 'package:eatvio_app/core/token_storage.dart';
import 'package:eatvio_app/core/widgets/show_error_dialog.dart';
import 'package:eatvio_app/features/registration/models/user_registration_model.dart'; import 'package:eatvio_app/features/registration/models/user_registration_model.dart';
import 'package:eatvio_app/features/registration/presentation/bloc/registration_cubit/registration_state.dart'; import 'package:eatvio_app/features/registration/presentation/bloc/registration_cubit/registration_state.dart';
import 'package:eatvio_app/features/registration/repositories/registration_repository.dart'; import 'package:eatvio_app/features/registration/repositories/registration_repository.dart';
@@ -50,17 +51,25 @@ class RegistrationCubit extends Cubit<RegistrationState> {
updateSheet(UserRegistrationSheet sheet) => emit(RegistrationState(sheet: sheet)); updateSheet(UserRegistrationSheet sheet) => emit(RegistrationState(sheet: sheet));
Future<void> tryRegistration() async { Future<void> tryRegistration() async {
try { if (await registrationRepository.emailIsReachable(state.sheet.email)) {
final token = await registrationRepository.tryRegister( try {
state.sheet.copyWith( final token = await registrationRepository.tryRegister(
phone: state.sheet.phone.replaceAll(' ', '').replaceAll('(', '').replaceAll(')', ''), state.sheet.copyWith(
responseBody: '{}', phone: state.sheet.phone.replaceAll(' ', '').replaceAll('(', '').replaceAll(')', ''),
), responseBody: '{}',
),
);
await TokenStorage.saveTokenInStorage(token);
AppRouter.navigatorKey.currentContext?.pushNamed('verifyEmailScreen');
} catch (e) {
log(e.toString());
}
} else {
showErrorDialog(
AppRouter.navigatorKey.currentContext!,
title: 'Ошибка регистрации',
description: 'Несуществующая почта.',
); );
await TokenStorage.saveTokenInStorage(token);
AppRouter.navigatorKey.currentContext?.pushNamed('verifyEmailScreen');
} catch (e) {
log(e.toString());
} }
} }

View File

@@ -1,3 +1,5 @@
import 'package:dio/dio.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/features/registration/models/user_registration_model.dart'; import 'package:eatvio_app/features/registration/models/user_registration_model.dart';
import 'package:eatvio_app/features/registration/models/location_model.dart'; import 'package:eatvio_app/features/registration/models/location_model.dart';
import 'package:eatvio_app/features/registration/services/registration_service.dart'; import 'package:eatvio_app/features/registration/services/registration_service.dart';
@@ -30,4 +32,17 @@ class RegistrationRepository {
rethrow; rethrow;
} }
} }
Future<bool> emailIsReachable(String email) async {
try {
final response = await sl<Dio>().getUri(Uri.parse('https://api.validate.email/validate?email=$email'));
if (response.data['result']['reachable'] != 'invalid') {
return true;
} else {
return false;
}
} catch (e) {
return false;
}
}
} }

View File

@@ -73,6 +73,19 @@ class MessageLookup extends MessageLookupByLibrary {
"allergies_presence": MessageLookupByLibrary.simpleMessage( "allergies_presence": MessageLookupByLibrary.simpleMessage(
"Наличие аллергии", "Наличие аллергии",
), ),
"allowAccess": MessageLookupByLibrary.simpleMessage("Разрешить доступ"),
"allowAccessToCamera": MessageLookupByLibrary.simpleMessage(
"Разрешите доступ к камере",
),
"allowAccessToCameraDescription": MessageLookupByLibrary.simpleMessage(
"Предоставление доступа позволит вам делать фотографии и добавлять к записям",
),
"allowAcessToGalery": MessageLookupByLibrary.simpleMessage(
"Разрешите доступ к фото",
),
"allowAcessToGaleryDescription": MessageLookupByLibrary.simpleMessage(
"Предоставление доступа позволит вам добавлять фото к записям",
),
"another_form_of_comment": MessageLookupByLibrary.simpleMessage( "another_form_of_comment": MessageLookupByLibrary.simpleMessage(
"комментария", "комментария",
), ),

View File

@@ -3344,6 +3344,56 @@ class S {
args: [], args: [],
); );
} }
/// `Разрешите доступ к фото`
String get allowAcessToGalery {
return Intl.message(
'Разрешите доступ к фото',
name: 'allowAcessToGalery',
desc: '',
args: [],
);
}
/// `Предоставление доступа позволит вам добавлять фото к записям`
String get allowAcessToGaleryDescription {
return Intl.message(
'Предоставление доступа позволит вам добавлять фото к записям',
name: 'allowAcessToGaleryDescription',
desc: '',
args: [],
);
}
/// `Разрешите доступ к камере`
String get allowAccessToCamera {
return Intl.message(
'Разрешите доступ к камере',
name: 'allowAccessToCamera',
desc: '',
args: [],
);
}
/// `Предоставление доступа позволит вам делать фотографии и добавлять к записям`
String get allowAccessToCameraDescription {
return Intl.message(
'Предоставление доступа позволит вам делать фотографии и добавлять к записям',
name: 'allowAccessToCameraDescription',
desc: '',
args: [],
);
}
/// `Разрешить доступ`
String get allowAccess {
return Intl.message(
'Разрешить доступ',
name: 'allowAccess',
desc: '',
args: [],
);
}
} }
class AppLocalizationDelegate extends LocalizationsDelegate<S> { class AppLocalizationDelegate extends LocalizationsDelegate<S> {

View File

@@ -397,6 +397,11 @@
"planStarted": "План «{planName}» начался", "planStarted": "План «{planName}» начался",
"userCanceldThePlanSubscription": "Пользователь {name} отменил подписку на план «{planName}»", "userCanceldThePlanSubscription": "Пользователь {name} отменил подписку на план «{planName}»",
"userSubscribedToPlan": "Пользователь {name} подписался на план «{planName}»", "userSubscribedToPlan": "Пользователь {name} подписался на план «{planName}»",
"markAsRead": "Отметить всё прочитанным" "markAsRead": "Отметить всё прочитанным",
"allowAcessToGalery": "Разрешите доступ к фото",
"allowAcessToGaleryDescription": "Предоставление доступа позволит вам добавлять фото к записям",
"allowAccessToCamera": "Разрешите доступ к камере",
"allowAccessToCameraDescription": "Предоставление доступа позволит вам делать фотографии и добавлять к записям",
"allowAccess": "Разрешить доступ"
} }

View File

@@ -21,7 +21,7 @@ import 'package:sentry_flutter/sentry_flutter.dart';
Future<void> main() async { Future<void> main() async {
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await di.init(); await di.init();
await SentryFlutter.init((options) { await SentryFlutter.init((options) {

View File

@@ -1,6 +1,6 @@
name: eatvio_app name: eatvio_app
description: A new Flutter project. description: A new Flutter project.
version: 1.1.2+32 version: 1.1.4+34
publish_to: 'none' publish_to: 'none'
environment: environment:
@@ -69,6 +69,8 @@ dependencies:
shared_preferences: ^2.5.3 shared_preferences: ^2.5.3
version: ^3.0.2 version: ^3.0.2
package_info_plus: ^8.3.0 package_info_plus: ^8.3.0
device_info_plus: ^11.4.0
permission_handler: ^12.0.0+1
dev_dependencies: dev_dependencies:
build_runner: ^2.4.15 build_runner: ^2.4.15