diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 2ebee285..0cacc747 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -388,7 +388,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
CODE_SIGN_STYLE = Manual;
- CURRENT_PROJECT_VERSION = 9;
+ CURRENT_PROJECT_VERSION = 12;
DEVELOPMENT_TEAM = VL68C36HYS;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@@ -538,7 +538,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
CODE_SIGN_STYLE = Manual;
- CURRENT_PROJECT_VERSION = 9;
+ CURRENT_PROJECT_VERSION = 12;
DEVELOPMENT_TEAM = VL68C36HYS;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
@@ -576,7 +576,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
CODE_SIGN_STYLE = Manual;
- CURRENT_PROJECT_VERSION = 9;
+ CURRENT_PROJECT_VERSION = 12;
DEVELOPMENT_TEAM = VL68C36HYS;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index eada9fab..52b35d63 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -21,7 +21,7 @@
CFBundleSignature
????
CFBundleVersion
- 9
+ 12
ITSAppUsesNonExemptEncryption
LSRequiresIPhoneOS
diff --git a/lib/core/locator_service.dart b/lib/core/locator_service.dart
index c2c27015..c82f70fb 100644
--- a/lib/core/locator_service.dart
+++ b/lib/core/locator_service.dart
@@ -107,7 +107,7 @@ Future init() async {
sl.registerFactory(() => ChatRoomCubit(cardsRepository: sl(), chatRepository: sl(), wsManager: sl()));
sl.registerFactory(() => RecipesCubit(feedRepository: sl(), recipeRepository: sl(), aboutRecipeRepository: sl()));
sl.registerFactory(() => CalendarCubit(calendarRepository: sl()));
- sl.registerFactory(() => ProfileCubit(profileRepository: sl(), chatRepository: sl()));
+ sl.registerFactory(() => ProfileCubit(profileRepository: sl(), chatRepository: sl(), consultantRepository: sl()));
sl.registerFactory(() => profile_plans.PlansCubit(plansRepository: sl()));
sl.registerFactory(() => DietsCubit(feedRepository: sl(), dietsRepository: sl()));
sl.registerFactory(() => CommentsCubit(feedRepository: sl()));
diff --git a/lib/features/consultant/become_consultant/models/consultation_request_model.dart b/lib/features/consultant/become_consultant/models/consultation_request_model.dart
new file mode 100644
index 00000000..aa7377b1
--- /dev/null
+++ b/lib/features/consultant/become_consultant/models/consultation_request_model.dart
@@ -0,0 +1,26 @@
+import 'package:eatvio_app/features/profile/models/profile_model.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'consultation_request_model.freezed.dart';
+
+part 'consultation_request_model.g.dart';
+
+@freezed
+class ConsultationRequestModel with _$ConsultationRequestModel {
+ const factory ConsultationRequestModel({
+ @JsonKey(name: 'rating_place') required int ratingPlace,
+ @JsonKey(name: 'education_resource') required EducationModel educationModel,
+ @JsonKey(name: 'cert_photos') required List photos,
+ }) = _ConsultationRequestModel;
+
+ factory ConsultationRequestModel.fromJson(Map json) => _$ConsultationRequestModelFromJson(json);
+}
+
+@freezed
+class CertPhotosModel with _$CertPhotosModel {
+ const factory CertPhotosModel({
+ required String url,
+ }) = _CertPhotosModel;
+
+ factory CertPhotosModel.fromJson(Map json) => _$CertPhotosModelFromJson(json);
+}
diff --git a/lib/features/consultant/become_consultant/repositories/consultant_repository.dart b/lib/features/consultant/become_consultant/repositories/consultant_repository.dart
index 3abdc799..6af88cb5 100644
--- a/lib/features/consultant/become_consultant/repositories/consultant_repository.dart
+++ b/lib/features/consultant/become_consultant/repositories/consultant_repository.dart
@@ -1,5 +1,6 @@
import 'dart:io';
import 'package:eatvio_app/features/consultant/become_consultant/models/consultant_profile_model.dart';
+import 'package:eatvio_app/features/consultant/become_consultant/models/consultation_request_model.dart';
import 'package:eatvio_app/features/consultant/become_consultant/services/consultant_service.dart';
import 'package:http_parser/http_parser.dart';
import 'package:dio/dio.dart';
@@ -33,7 +34,9 @@ class ConsultantRepository {
final fileBytes = await file.readAsBytes();
final multipartFile = MultipartFile.fromBytes(
fileBytes,
- filename: file.path.split('/').last,
+ filename: file.path
+ .split('/')
+ .last,
contentType: MediaType('application', 'octet-stream'),
);
multipartFiles.add(multipartFile);
@@ -46,4 +49,12 @@ class ConsultantRepository {
rethrow;
}
}
+
+ Future getConsultantRequestStatus(String slug) async {
+ try {
+ return await consultantService.getConsultantRequestStatus(slug: slug);
+ } catch (e) {
+ rethrow;
+ }
+ }
}
diff --git a/lib/features/consultant/become_consultant/services/consultant_service.dart b/lib/features/consultant/become_consultant/services/consultant_service.dart
index 4479122f..cc284993 100644
--- a/lib/features/consultant/become_consultant/services/consultant_service.dart
+++ b/lib/features/consultant/become_consultant/services/consultant_service.dart
@@ -1,5 +1,6 @@
import 'package:dio/dio.dart';
import 'package:eatvio_app/features/consultant/become_consultant/models/consultant_profile_model.dart';
+import 'package:eatvio_app/features/consultant/become_consultant/models/consultation_request_model.dart';
import 'package:retrofit/retrofit.dart';
part 'consultant_service.g.dart';
@@ -18,4 +19,9 @@ abstract class ConsultantService {
Future> uploadPhotos({
@Part(name: 'photos[]', contentType: 'multipart/form-data') List? photos,
});
+
+ @GET('/api/consultant_request/{slug}/status')
+ Future getConsultantRequestStatus({
+ @Path() required String slug,
+ });
}
diff --git a/lib/features/profile/models/sub_model.dart b/lib/features/profile/models/sub_model.dart
index c4a04bd7..0a00bc0d 100644
--- a/lib/features/profile/models/sub_model.dart
+++ b/lib/features/profile/models/sub_model.dart
@@ -26,7 +26,6 @@ class SubModel with _$SubModel {
@JsonKey(name: 'is_entity_subscribed_by_me') required bool isEntitySubscribedByMe,
@JsonKey(name: 'profile_photo_url') required String profilePhotoUrl,
required List roles,
- @JsonKey(name: 'is_public_profile') required bool isPublicProfile,
}) = _SubModel;
factory SubModel.fromJson(Map json) => _$SubModelFromJson(json);
diff --git a/lib/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart b/lib/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart
index 71fb1729..cdbab793 100644
--- a/lib/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart
+++ b/lib/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart
@@ -2,6 +2,7 @@ import 'dart:developer';
import 'dart:io';
import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart';
import 'package:eatvio_app/features/chat/repositories/chat_repository.dart';
+import 'package:eatvio_app/features/consultant/become_consultant/repositories/consultant_repository.dart';
import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart';
import 'package:eatvio_app/features/profile/presentation/bloc/profile_cubit/profile_state.dart';
import 'package:eatvio_app/features/profile/repositories/profile_repository.dart';
@@ -10,20 +11,24 @@ import 'package:flutter_bloc/flutter_bloc.dart';
class ProfileCubit extends Cubit {
final ProfileRepository profileRepository;
final ChatRepository chatRepository;
+ final ConsultantRepository consultantRepository;
String _currentPeriod = '1';
ProfileCubit({
required this.profileRepository,
required this.chatRepository,
+ required this.consultantRepository,
}) : super(const ProfileState.loading());
Future getProfileBySlug(String slug) async {
try {
+ final profile = await profileRepository.getProfileBySlug(slug);
emit(
ProfileState.loaded(
- profile: await profileRepository.getProfileBySlug(slug),
+ profile: profile,
profileDetails: await profileRepository.getProfileDetailsBySlug(slug),
blogPosts: await profileRepository.getBlogPostsByAuthorSlug(slug: slug),
+ consultantInfo: profile.roles.contains('consultant') ? await consultantRepository.getConsultantRequestStatus(slug) : null,
),
);
} catch (e) {
@@ -44,6 +49,7 @@ class ProfileCubit extends Cubit {
blogPosts: await profileRepository.getBlogPostsByAuthorSlug(slug: profile.slug),
weightPoints: await profileRepository.getWeight(period: period),
circumferencePoints: await profileRepository.getCircumferences(period: period),
+ consultantInfo: profile.roles.contains('consultant') ? await consultantRepository.getConsultantRequestStatus(profile.slug) : null,
),
);
} catch (e, stackTrace) {
diff --git a/lib/features/profile/presentation/bloc/profile_cubit/profile_state.dart b/lib/features/profile/presentation/bloc/profile_cubit/profile_state.dart
index ec7fd13d..6e8154fc 100644
--- a/lib/features/profile/presentation/bloc/profile_cubit/profile_state.dart
+++ b/lib/features/profile/presentation/bloc/profile_cubit/profile_state.dart
@@ -1,3 +1,4 @@
+import 'package:eatvio_app/features/consultant/become_consultant/models/consultation_request_model.dart';
import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart';
import 'package:eatvio_app/features/profile/models/circumference_model.dart';
import 'package:eatvio_app/features/profile/models/profile_model.dart';
@@ -18,6 +19,7 @@ abstract class ProfileState with _$ProfileState {
required List blogPosts,
List? weightPoints,
List? circumferencePoints,
+ ConsultationRequestModel? consultantInfo,
}) = ProfileStateLoaded;
const factory ProfileState.error({
diff --git a/lib/features/profile/presentation/pages/profile_screen.dart b/lib/features/profile/presentation/pages/profile_screen.dart
index df96f608..699aa021 100644
--- a/lib/features/profile/presentation/pages/profile_screen.dart
+++ b/lib/features/profile/presentation/pages/profile_screen.dart
@@ -93,7 +93,9 @@ class _ProfileScreenState extends State {
} else if (state is ProfileStateError) {
return DataErrorWidget(
onReload: () async {
- widget.author == null || widget.author!.authorIsMe ? await profileCubit.getMyProfile() : await profileCubit.getProfileBySlug(widget.author!.authorSlug);
+ widget.author == null || widget.author!.authorIsMe
+ ? await profileCubit.getMyProfile()
+ : await profileCubit.getProfileBySlug(widget.author!.authorSlug);
},
);
} else {
@@ -144,25 +146,24 @@ class _ProfileScreenState extends State {
),
),
const SizedBox(width: 12),
- if (state.profile.ratingPlace != null)
- Container(
- padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
- decoration: BoxDecoration(
- color: const Color(0xFFF6FAFF),
- borderRadius: BorderRadius.circular(12),
- ),
- child: Text(
- '${state.profile.ratingPlace!}-й в рейтинге',
- style: TextStyle(
- fontSize: 16,
- fontWeight: FontWeight.w600,
- color: appTheme.blueA400,
- ),
+ Container(
+ padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
+ decoration: BoxDecoration(
+ color: const Color(0xFFF6FAFF),
+ borderRadius: BorderRadius.circular(12),
+ ),
+ child: Text(
+ '${state.consultantInfo!.ratingPlace}-й в рейтинге',
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w600,
+ color: appTheme.blueA400,
),
),
+ ),
],
),
- if (state.profile.educationResource?.hasMedicalEducation == 1)
+ if (state.consultantInfo!.educationModel.hasMedicalEducation == 1)
const Row(
children: [
SizedBox(width: 16),
@@ -221,6 +222,61 @@ class _ProfileScreenState extends State {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_header(state),
+ if (state.profile.roles.contains('consultant'))
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ const SizedBox(width: 16),
+ const Text(
+ 'Консультант',
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w600,
+ color: Color(
+ 0xFF323F4B,
+ ),
+ ),
+ ),
+ const SizedBox(width: 12),
+ Container(
+ padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
+ decoration: BoxDecoration(
+ color: const Color(0xFFF6FAFF),
+ borderRadius: BorderRadius.circular(12),
+ ),
+ child: Text(
+ '${state.consultantInfo!.ratingPlace}-й в рейтинге',
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w600,
+ color: appTheme.blueA400,
+ ),
+ ),
+ ),
+ ],
+ ),
+ if (state.consultantInfo!.educationModel.hasMedicalEducation == 1)
+ const Row(
+ children: [
+ SizedBox(width: 16),
+ Icon(
+ Icons.done_rounded,
+ color: Colors.green,
+ ),
+ Text(
+ 'Медицинское образование',
+ style: TextStyle(
+ color: Color(0xFF323F4B),
+ fontSize: 14,
+ fontWeight: FontWeight.w600,
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
if (state.profileDetails.additionalInfo.isNotEmpty) _additionalInfo(state),
],
),
@@ -371,7 +427,7 @@ class _ProfileScreenState extends State {
}
_diplomasAndCertificates(ProfileStateLoaded state) {
- if (!state.profile.roles.contains('consultant') || state.profile.diplomasAndCertificates == null || state.profile.diplomasAndCertificates!.isEmpty) {
+ if (!state.profile.roles.contains('consultant') || state.consultantInfo!.photos.isEmpty) {
return const SizedBox.shrink();
}
return Padding(
@@ -416,8 +472,18 @@ class _ProfileScreenState extends State {
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
- if (state.profile.diplomasAndCertificates != null)
- ...state.profile.diplomasAndCertificates!.map((d) => _diplomaImage('$baseUrl/storage/${d.path}')),
+ if (state.consultantInfo!.photos.isNotEmpty)
+ ...List.generate(
+ state.consultantInfo!.photos.length * 2 - 1,
+ (index) {
+ if (index.isEven) {
+ final photo = state.consultantInfo!.photos[index ~/ 2];
+ return _diplomaImage(photo.url);
+ } else {
+ return const SizedBox(width: 8);
+ }
+ },
+ ),
],
),
),
@@ -456,8 +522,8 @@ class _ProfileScreenState extends State {
_education(ProfileStateLoaded state) {
if (!state.profile.roles.contains('consultant') ||
- state.profile.educationResource?.completedCourses == null ||
- (state.profile.educationResource?.completedCourses?.isEmpty ?? false)) {
+ state.consultantInfo!.educationModel.completedCourses == null ||
+ (state.consultantInfo!.educationModel.completedCourses?.isEmpty ?? false)) {
return const SizedBox.shrink();
}
return Padding(
@@ -495,11 +561,17 @@ class _ProfileScreenState extends State {
color: Colors.white,
borderRadius: BorderRadius.circular(12),
child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
+ padding: const EdgeInsets.only(left: 16, right: 16, top: 20),
child: Column(
children: [
- if (state.profile.educationResource?.completedCourses != null)
- ...state.profile.educationResource!.completedCourses!.map((c) => _educationBlock(c.values.first.title, c.values.first.date)),
+ ListView.separated(
+ shrinkWrap: true,
+ physics: const NeverScrollableScrollPhysics(),
+ itemBuilder: (context, index) => _educationBlock(state.consultantInfo!.educationModel.completedCourses![index].values.first.title,
+ state.consultantInfo!.educationModel.completedCourses![index].values.first.date),
+ separatorBuilder: (context, index) => const SizedBox(height: 8),
+ itemCount: state.consultantInfo!.educationModel.completedCourses!.length,
+ ),
],
),
),
@@ -564,39 +636,39 @@ class _ProfileScreenState extends State {
() {
launchUrl(
Uri.parse(
- state.profile.educationResource?.vkLink ?? '',
+ state.consultantInfo!.educationModel.vkLink ?? '',
),
mode: LaunchMode.externalApplication,
);
},
'assets/icons/vk.svg',
- state.profile.educationResource?.vkLink == null ? true : false,
+ state.consultantInfo!.educationModel.vkLink!.isEmpty ? true : false,
),
const SizedBox(width: 12),
_socialLinkButton(
() {
launchUrl(
Uri.parse(
- state.profile.educationResource?.vkLink ?? '',
+ state.consultantInfo!.educationModel.tgLink ?? '',
),
mode: LaunchMode.externalApplication,
);
},
'assets/icons/telegram.svg',
- state.profile.educationResource?.vkLink == null ? true : false,
+ state.consultantInfo!.educationModel.tgLink!.isEmpty ? true : false,
),
const SizedBox(width: 12),
_socialLinkButton(
() {
launchUrl(
Uri.parse(
- state.profile.educationResource?.personalSiteLink ?? '',
+ state.consultantInfo!.educationModel.personalSiteLink ?? '',
),
mode: LaunchMode.externalApplication,
);
},
'assets/icons/web.svg',
- state.profile.educationResource?.personalSiteLink == null ? true : false,
+ state.consultantInfo!.educationModel.personalSiteLink!.isEmpty ? true : false,
),
],
),
@@ -620,7 +692,7 @@ class _ProfileScreenState extends State {
child: SvgPicture.asset(
icon,
width: 24,
- colorFilter: const ColorFilter.mode(Colors.black12, BlendMode.srcIn),
+ colorFilter: disabled ? const ColorFilter.mode(Colors.black12, BlendMode.srcIn) : const ColorFilter.mode(Colors.black, BlendMode.srcIn),
),
),
),
@@ -659,7 +731,7 @@ class _ProfileScreenState extends State {
for (int i = 0; i < state.profile.badges.length; i++) ...[
state.profile.badges[i].image.contains('svg')
? Tooltip(
- message: state.profile.badges[i].description,
+ message: state.profile.badges[i].description,
triggerMode: TooltipTriggerMode.tap,
textStyle: const TextStyle(
color: Colors.black,