refactor: improve specialization card layout and unify chat screen header and navigation with home screen components.

This commit is contained in:
pradeepkumar
2026-03-08 02:01:03 +05:30
parent bba9cd3936
commit 219acfc013
14 changed files with 2498 additions and 236 deletions

View File

@@ -0,0 +1,66 @@
import 'package:dio/dio.dart';
import 'package:real_estate_mobile/core/network/api_client.dart';
import 'package:real_estate_mobile/core/network/api_exceptions.dart';
class ProfileRepository {
final Dio _dio = ApiClient.instance.dio;
/// Fetch profile based on role.
/// Agent: GET /agents/profile/me
/// User: GET /users/profile/me
Future<Map<String, dynamic>> getMyProfile(String role) async {
try {
final endpoint = role == 'AGENT'
? '/agents/profile/me'
: '/users/profile/me';
final response = await _dio.get(endpoint);
return response.data['data'] as Map<String, dynamic>;
} on DioException catch (e) {
if (e.error is ApiException) throw e.error as ApiException;
throw ApiException(
message: e.message ?? 'Failed to fetch profile',
statusCode: e.response?.statusCode,
);
}
}
/// Update profile based on role.
/// Agent: PUT /agents/profile
/// User: PATCH /users/profile/me
Future<Map<String, dynamic>> updateProfile(
String role,
Map<String, dynamic> data,
) async {
try {
final Response response;
if (role == 'AGENT') {
response = await _dio.put('/agents/profile', data: data);
} else {
response = await _dio.patch('/users/profile/me', data: data);
}
return response.data['data'] as Map<String, dynamic>;
} on DioException catch (e) {
if (e.error is ApiException) throw e.error as ApiException;
throw ApiException(
message: e.message ?? 'Failed to update profile',
statusCode: e.response?.statusCode,
);
}
}
Future<String> getAvatarUploadUrl(String role, String fileName) async {
try {
final endpoint = role == 'AGENT'
? '/upload/avatar-presigned-url'
: '/upload/user-avatar-presigned-url';
final response = await _dio.post(endpoint, data: {'fileName': fileName});
return response.data['data']['url'] as String;
} on DioException catch (e) {
if (e.error is ApiException) throw e.error as ApiException;
throw ApiException(
message: e.message ?? 'Failed to get upload URL',
statusCode: e.response?.statusCode,
);
}
}
}

View File

@@ -0,0 +1,98 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:real_estate_mobile/features/auth/presentation/providers/auth_provider.dart';
import 'package:real_estate_mobile/features/profile/data/profile_repository.dart';
// Profile state
class ProfileState {
final String role;
final Map<String, dynamic> profile;
final bool isLoading;
final bool isSaving;
final String? errorMessage;
final String? successMessage;
const ProfileState({
this.role = 'USER',
this.profile = const {},
this.isLoading = true,
this.isSaving = false,
this.errorMessage,
this.successMessage,
});
bool get isAgent => role == 'AGENT';
ProfileState copyWith({
String? role,
Map<String, dynamic>? profile,
bool? isLoading,
bool? isSaving,
String? errorMessage,
String? successMessage,
bool clearError = false,
bool clearSuccess = false,
}) {
return ProfileState(
role: role ?? this.role,
profile: profile ?? this.profile,
isLoading: isLoading ?? this.isLoading,
isSaving: isSaving ?? this.isSaving,
errorMessage: clearError ? null : (errorMessage ?? this.errorMessage),
successMessage:
clearSuccess ? null : (successMessage ?? this.successMessage),
);
}
}
class ProfileNotifier extends StateNotifier<ProfileState> {
final ProfileRepository _repository;
final String _role;
ProfileNotifier(this._repository, this._role)
: super(ProfileState(role: _role)) {
loadProfile();
}
Future<void> loadProfile() async {
state = state.copyWith(isLoading: true, clearError: true);
try {
final profile = await _repository.getMyProfile(_role);
state = state.copyWith(profile: profile, isLoading: false);
} catch (e) {
state = state.copyWith(
isLoading: false,
errorMessage: e.toString(),
);
}
}
Future<void> updateProfile(Map<String, dynamic> data) async {
state =
state.copyWith(isSaving: true, clearError: true, clearSuccess: true);
try {
final updated = await _repository.updateProfile(_role, data);
state = state.copyWith(
profile: updated,
isSaving: false,
successMessage: 'Profile updated successfully',
);
} catch (e) {
state = state.copyWith(
isSaving: false,
errorMessage: 'Failed to update profile',
);
}
}
}
// Providers
final profileRepositoryProvider = Provider<ProfileRepository>((ref) {
return ProfileRepository();
});
final profileProvider =
StateNotifierProvider.autoDispose<ProfileNotifier, ProfileState>((ref) {
final authState = ref.watch(authProvider);
final role = authState.user?.role ?? 'USER';
return ProfileNotifier(ref.watch(profileRepositoryProvider), role);
});

File diff suppressed because it is too large Load Diff