Refactor PersonalInfoRepository to use Firebase Auth for user profile retrieval and remove mock implementation
This commit is contained in:
committed by
José Salazar
parent
fef3289648
commit
582855d86b
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:firebase_auth/firebase_auth.dart';
|
||||||
import 'package:firebase_data_connect/firebase_data_connect.dart';
|
import 'package:firebase_data_connect/firebase_data_connect.dart';
|
||||||
import 'package:krow_data_connect/krow_data_connect.dart';
|
import 'package:krow_data_connect/krow_data_connect.dart';
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
@@ -14,27 +15,36 @@ import '../../domain/repositories/personal_info_repository_interface.dart';
|
|||||||
/// - Containing no business logic
|
/// - Containing no business logic
|
||||||
class PersonalInfoRepositoryImpl implements PersonalInfoRepositoryInterface {
|
class PersonalInfoRepositoryImpl implements PersonalInfoRepositoryInterface {
|
||||||
final ExampleConnector _dataConnect;
|
final ExampleConnector _dataConnect;
|
||||||
|
final FirebaseAuth _firebaseAuth;
|
||||||
|
|
||||||
/// Creates a [PersonalInfoRepositoryImpl].
|
/// Creates a [PersonalInfoRepositoryImpl].
|
||||||
///
|
///
|
||||||
/// Requires the Firebase Data Connect connector instance.
|
/// Requires the Firebase Data Connect connector instance and Firebase Auth.
|
||||||
PersonalInfoRepositoryImpl({
|
PersonalInfoRepositoryImpl({
|
||||||
required ExampleConnector dataConnect,
|
required ExampleConnector dataConnect,
|
||||||
}) : _dataConnect = dataConnect;
|
required FirebaseAuth firebaseAuth,
|
||||||
|
}) : _dataConnect = dataConnect,
|
||||||
|
_firebaseAuth = firebaseAuth;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Staff> getStaffProfile(String staffId) async {
|
Future<Staff> getStaffProfile() async {
|
||||||
// Query staff data from Firebase Data Connect
|
final user = _firebaseAuth.currentUser;
|
||||||
final QueryResult<GetStaffByIdData, GetStaffByIdVariables> result =
|
if (user == null) {
|
||||||
await _dataConnect.getStaffById(id: staffId).execute();
|
throw Exception('User not authenticated');
|
||||||
|
|
||||||
final staff = result.data.staff;
|
|
||||||
if (staff == null) {
|
|
||||||
throw Exception('Staff profile not found for ID: $staffId');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Query staff data from Firebase Data Connect
|
||||||
|
final QueryResult<GetStaffByUserIdData, GetStaffByUserIdVariables> result =
|
||||||
|
await _dataConnect.getStaffByUserId(userId: user.uid).execute();
|
||||||
|
|
||||||
|
if (result.data.staffs.isEmpty) {
|
||||||
|
throw Exception('Staff profile not found for User ID: ${user.uid}');
|
||||||
|
}
|
||||||
|
|
||||||
|
final rawStaff = result.data.staffs.first;
|
||||||
|
|
||||||
// Map from data_connect DTO to domain entity
|
// Map from data_connect DTO to domain entity
|
||||||
return _mapToStaffEntity(staff);
|
return _mapToStaffEntity(rawStaff);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -54,7 +64,7 @@ class PersonalInfoRepositoryImpl implements PersonalInfoRepositoryInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the updated staff profile to return complete entity
|
// Fetch the updated staff profile to return complete entity
|
||||||
return getStaffProfile(staff.id);
|
return getStaffProfile();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -69,16 +79,22 @@ class PersonalInfoRepositoryImpl implements PersonalInfoRepositoryInterface {
|
|||||||
/// Maps a data_connect Staff DTO to a domain Staff entity.
|
/// Maps a data_connect Staff DTO to a domain Staff entity.
|
||||||
///
|
///
|
||||||
/// This mapping isolates the domain from data layer implementation details.
|
/// This mapping isolates the domain from data layer implementation details.
|
||||||
Staff _mapToStaffEntity(GetStaffByIdStaff dto) {
|
Staff _mapToStaffEntity(GetStaffByUserIdStaffs dto) {
|
||||||
return Staff(
|
return Staff(
|
||||||
id: dto.id,
|
id: dto.id,
|
||||||
authProviderId: dto.userId,
|
authProviderId: dto.userId,
|
||||||
name: dto.fullName,
|
name: dto.fullName,
|
||||||
email: dto.email ?? '',
|
email: dto.email ?? '',
|
||||||
phone: dto.phone,
|
phone: dto.phone,
|
||||||
status: StaffStatus.active, // TODO: Map from actual status field when available
|
|
||||||
address: dto.addres,
|
|
||||||
avatar: dto.photoUrl,
|
avatar: dto.photoUrl,
|
||||||
|
status: StaffStatus.active,
|
||||||
|
address: dto.addres,
|
||||||
|
totalShifts: dto.totalShifts,
|
||||||
|
averageRating: dto.averageRating,
|
||||||
|
onTimeRate: dto.onTimeRate,
|
||||||
|
noShowCount: dto.noShowCount,
|
||||||
|
cancellationCount: dto.cancellationCount,
|
||||||
|
reliabilityScore: dto.reliabilityScore,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
import 'package:krow_domain/krow_domain.dart';
|
|
||||||
|
|
||||||
import '../../domain/repositories/personal_info_repository_interface.dart';
|
|
||||||
|
|
||||||
/// Mock implementation of [PersonalInfoRepositoryInterface].
|
|
||||||
///
|
|
||||||
/// This mock repository returns hardcoded data for development
|
|
||||||
/// and will be replaced with [PersonalInfoRepositoryImpl] when
|
|
||||||
/// Firebase Data Connect is fully configured.
|
|
||||||
///
|
|
||||||
/// Following Clean Architecture, this mock:
|
|
||||||
/// - Implements the domain repository interface
|
|
||||||
/// - Returns domain entities (Staff)
|
|
||||||
/// - Simulates async operations with delays
|
|
||||||
/// - Provides realistic test data
|
|
||||||
class PersonalInfoRepositoryMock implements PersonalInfoRepositoryInterface {
|
|
||||||
// Simulated in-memory storage
|
|
||||||
Staff? _cachedStaff;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Staff> getStaffProfile(String staffId) async {
|
|
||||||
// Simulate network delay
|
|
||||||
await Future.delayed(const Duration(milliseconds: 500));
|
|
||||||
|
|
||||||
// Return cached staff or create mock data
|
|
||||||
return _cachedStaff ??
|
|
||||||
const Staff(
|
|
||||||
id: 'mock-staff-1',
|
|
||||||
authProviderId: 'mock-auth-1',
|
|
||||||
name: 'Krower',
|
|
||||||
email: 'worker@krow.com',
|
|
||||||
phone: '',
|
|
||||||
status: StaffStatus.active,
|
|
||||||
address: 'Montreal, Quebec',
|
|
||||||
avatar: null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Staff> updateStaffProfile(Staff staff) async {
|
|
||||||
// Simulate network delay
|
|
||||||
await Future.delayed(const Duration(milliseconds: 800));
|
|
||||||
|
|
||||||
// Store in cache
|
|
||||||
_cachedStaff = staff;
|
|
||||||
|
|
||||||
// Return the updated staff
|
|
||||||
return staff;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<String> uploadProfilePhoto(String filePath) async {
|
|
||||||
// Simulate upload delay
|
|
||||||
await Future.delayed(const Duration(seconds: 2));
|
|
||||||
|
|
||||||
// Return a mock URL
|
|
||||||
return 'https://example.com/photos/${DateTime.now().millisecondsSinceEpoch}.jpg';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,10 +8,10 @@ import 'package:krow_domain/krow_domain.dart';
|
|||||||
/// Implementations must delegate all data operations through
|
/// Implementations must delegate all data operations through
|
||||||
/// the data_connect layer, following Clean Architecture principles.
|
/// the data_connect layer, following Clean Architecture principles.
|
||||||
abstract interface class PersonalInfoRepositoryInterface {
|
abstract interface class PersonalInfoRepositoryInterface {
|
||||||
/// Retrieves the staff profile for the specified staff ID.
|
/// Retrieves the staff profile for the current authenticated user.
|
||||||
///
|
///
|
||||||
/// Returns the complete [Staff] entity with all profile information.
|
/// Returns the complete [Staff] entity with all profile information.
|
||||||
Future<Staff> getStaffProfile(String staffId);
|
Future<Staff> getStaffProfile();
|
||||||
|
|
||||||
/// Updates the staff profile information.
|
/// Updates the staff profile information.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -2,23 +2,12 @@ import 'package:krow_core/core.dart';
|
|||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
import '../repositories/personal_info_repository_interface.dart';
|
import '../repositories/personal_info_repository_interface.dart';
|
||||||
|
|
||||||
/// Arguments for getting staff profile information.
|
|
||||||
class GetPersonalInfoArguments extends UseCaseArgument {
|
|
||||||
/// The staff member's ID.
|
|
||||||
final String staffId;
|
|
||||||
|
|
||||||
const GetPersonalInfoArguments({required this.staffId});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [staffId];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Use case for retrieving staff profile information.
|
/// Use case for retrieving staff profile information.
|
||||||
///
|
///
|
||||||
/// This use case fetches the complete staff profile from the repository,
|
/// This use case fetches the complete staff profile from the repository,
|
||||||
/// which delegates to the data_connect layer for data access.
|
/// which delegates to the data_connect layer for data access.
|
||||||
class GetPersonalInfoUseCase
|
class GetPersonalInfoUseCase
|
||||||
implements UseCase<GetPersonalInfoArguments, Staff> {
|
implements NoInputUseCase<Staff> {
|
||||||
final PersonalInfoRepositoryInterface _repository;
|
final PersonalInfoRepositoryInterface _repository;
|
||||||
|
|
||||||
/// Creates a [GetPersonalInfoUseCase].
|
/// Creates a [GetPersonalInfoUseCase].
|
||||||
@@ -27,7 +16,7 @@ class GetPersonalInfoUseCase
|
|||||||
GetPersonalInfoUseCase(this._repository);
|
GetPersonalInfoUseCase(this._repository);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Staff> call(GetPersonalInfoArguments arguments) {
|
Future<Staff> call() {
|
||||||
return _repository.getStaffProfile(arguments.staffId);
|
return _repository.getStaffProfile();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,23 +2,12 @@ import 'package:krow_core/core.dart';
|
|||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
import '../repositories/personal_info_repository_interface.dart';
|
import '../repositories/personal_info_repository_interface.dart';
|
||||||
|
|
||||||
/// Arguments for updating staff profile information.
|
|
||||||
class UpdatePersonalInfoArguments extends UseCaseArgument {
|
|
||||||
/// The staff entity with updated information.
|
|
||||||
final Staff staff;
|
|
||||||
|
|
||||||
const UpdatePersonalInfoArguments({required this.staff});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [staff];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Use case for updating staff profile information.
|
/// Use case for updating staff profile information.
|
||||||
///
|
///
|
||||||
/// This use case updates the staff profile information
|
/// This use case updates the staff profile information
|
||||||
/// through the repository, which delegates to the data_connect layer.
|
/// through the repository, which delegates to the data_connect layer.
|
||||||
class UpdatePersonalInfoUseCase
|
class UpdatePersonalInfoUseCase
|
||||||
implements UseCase<UpdatePersonalInfoArguments, Staff> {
|
implements UseCase<Staff, Staff> {
|
||||||
final PersonalInfoRepositoryInterface _repository;
|
final PersonalInfoRepositoryInterface _repository;
|
||||||
|
|
||||||
/// Creates an [UpdatePersonalInfoUseCase].
|
/// Creates an [UpdatePersonalInfoUseCase].
|
||||||
@@ -27,7 +16,7 @@ class UpdatePersonalInfoUseCase
|
|||||||
UpdatePersonalInfoUseCase(this._repository);
|
UpdatePersonalInfoUseCase(this._repository);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Staff> call(UpdatePersonalInfoArguments arguments) {
|
Future<Staff> call(Staff arguments) {
|
||||||
return _repository.updateStaffProfile(arguments.staff);
|
return _repository.updateStaffProfile(arguments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
|
import 'package:krow_core/core.dart';
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
|
||||||
import '../../domain/usecases/get_personal_info_usecase.dart';
|
import '../../domain/usecases/get_personal_info_usecase.dart';
|
||||||
@@ -16,18 +17,15 @@ class PersonalInfoBloc extends Bloc<PersonalInfoEvent, PersonalInfoState>
|
|||||||
implements Disposable {
|
implements Disposable {
|
||||||
final GetPersonalInfoUseCase _getPersonalInfoUseCase;
|
final GetPersonalInfoUseCase _getPersonalInfoUseCase;
|
||||||
final UpdatePersonalInfoUseCase _updatePersonalInfoUseCase;
|
final UpdatePersonalInfoUseCase _updatePersonalInfoUseCase;
|
||||||
final String _staffId;
|
|
||||||
|
|
||||||
/// Creates a [PersonalInfoBloc].
|
/// Creates a [PersonalInfoBloc].
|
||||||
///
|
///
|
||||||
/// Requires the staff ID to load and update the correct profile.
|
/// Requires the use cases to load and update the profile.
|
||||||
PersonalInfoBloc({
|
PersonalInfoBloc({
|
||||||
required GetPersonalInfoUseCase getPersonalInfoUseCase,
|
required GetPersonalInfoUseCase getPersonalInfoUseCase,
|
||||||
required UpdatePersonalInfoUseCase updatePersonalInfoUseCase,
|
required UpdatePersonalInfoUseCase updatePersonalInfoUseCase,
|
||||||
required String staffId,
|
|
||||||
}) : _getPersonalInfoUseCase = getPersonalInfoUseCase,
|
}) : _getPersonalInfoUseCase = getPersonalInfoUseCase,
|
||||||
_updatePersonalInfoUseCase = updatePersonalInfoUseCase,
|
_updatePersonalInfoUseCase = updatePersonalInfoUseCase,
|
||||||
_staffId = staffId,
|
|
||||||
super(const PersonalInfoState()) {
|
super(const PersonalInfoState()) {
|
||||||
on<PersonalInfoLoadRequested>(_onLoadRequested);
|
on<PersonalInfoLoadRequested>(_onLoadRequested);
|
||||||
on<PersonalInfoFieldUpdated>(_onFieldUpdated);
|
on<PersonalInfoFieldUpdated>(_onFieldUpdated);
|
||||||
@@ -42,9 +40,7 @@ class PersonalInfoBloc extends Bloc<PersonalInfoEvent, PersonalInfoState>
|
|||||||
) async {
|
) async {
|
||||||
emit(state.copyWith(status: PersonalInfoStatus.loading));
|
emit(state.copyWith(status: PersonalInfoStatus.loading));
|
||||||
try {
|
try {
|
||||||
final Staff staff = await _getPersonalInfoUseCase(
|
final Staff staff = await _getPersonalInfoUseCase();
|
||||||
GetPersonalInfoArguments(staffId: _staffId),
|
|
||||||
);
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
status: PersonalInfoStatus.loaded,
|
status: PersonalInfoStatus.loaded,
|
||||||
staff: staff,
|
staff: staff,
|
||||||
@@ -115,7 +111,7 @@ class PersonalInfoBloc extends Bloc<PersonalInfoEvent, PersonalInfoState>
|
|||||||
emit(state.copyWith(status: PersonalInfoStatus.saving));
|
emit(state.copyWith(status: PersonalInfoStatus.saving));
|
||||||
try {
|
try {
|
||||||
final Staff updatedStaff = await _updatePersonalInfoUseCase(
|
final Staff updatedStaff = await _updatePersonalInfoUseCase(
|
||||||
UpdatePersonalInfoArguments(staff: state.staff!),
|
state.staff!,
|
||||||
);
|
);
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
status: PersonalInfoStatus.saved,
|
status: PersonalInfoStatus.saved,
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
import 'package:firebase_auth/firebase_auth.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
|
import 'package:krow_data_connect/krow_data_connect.dart';
|
||||||
|
|
||||||
import 'data/repositories/personal_info_repository_mock.dart';
|
import 'data/repositories/personal_info_repository_impl.dart';
|
||||||
import 'domain/repositories/personal_info_repository_interface.dart';
|
import 'domain/repositories/personal_info_repository_interface.dart';
|
||||||
import 'domain/usecases/get_personal_info_usecase.dart';
|
import 'domain/usecases/get_personal_info_usecase.dart';
|
||||||
import 'domain/usecases/update_personal_info_usecase.dart';
|
import 'domain/usecases/update_personal_info_usecase.dart';
|
||||||
@@ -14,17 +16,19 @@ import 'presentation/pages/personal_info_page.dart';
|
|||||||
/// personal information functionality following Clean Architecture.
|
/// personal information functionality following Clean Architecture.
|
||||||
///
|
///
|
||||||
/// The module:
|
/// The module:
|
||||||
/// - Registers repository implementations (mock for now, will use real impl later)
|
/// - Registers repository implementations
|
||||||
/// - Registers use cases that contain business logic
|
/// - Registers use cases that contain business logic
|
||||||
/// - Registers BLoC for state management
|
/// - Registers BLoC for state management
|
||||||
/// - Defines routes for navigation
|
/// - Defines routes for navigation
|
||||||
class StaffProfileInfoModule extends Module {
|
class StaffProfileInfoModule extends Module {
|
||||||
@override
|
@override
|
||||||
void binds(Injector i) {
|
void binds(Injector i) {
|
||||||
// Repository - using mock for now
|
// Repository
|
||||||
// TODO: Replace with PersonalInfoRepositoryImpl when Firebase Data Connect is configured
|
|
||||||
i.addLazySingleton<PersonalInfoRepositoryInterface>(
|
i.addLazySingleton<PersonalInfoRepositoryInterface>(
|
||||||
PersonalInfoRepositoryMock.new,
|
() => PersonalInfoRepositoryImpl(
|
||||||
|
dataConnect: ExampleConnector.instance,
|
||||||
|
firebaseAuth: FirebaseAuth.instance,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Use Cases - delegate business logic to repository
|
// Use Cases - delegate business logic to repository
|
||||||
@@ -36,12 +40,10 @@ class StaffProfileInfoModule extends Module {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// BLoC - manages presentation state
|
// BLoC - manages presentation state
|
||||||
// TODO: Get actual staffId from authentication state
|
|
||||||
i.addLazySingleton<PersonalInfoBloc>(
|
i.addLazySingleton<PersonalInfoBloc>(
|
||||||
() => PersonalInfoBloc(
|
() => PersonalInfoBloc(
|
||||||
getPersonalInfoUseCase: i.get<GetPersonalInfoUseCase>(),
|
getPersonalInfoUseCase: i.get<GetPersonalInfoUseCase>(),
|
||||||
updatePersonalInfoUseCase: i.get<UpdatePersonalInfoUseCase>(),
|
updatePersonalInfoUseCase: i.get<UpdatePersonalInfoUseCase>(),
|
||||||
staffId: 'mock-staff-1', // TODO: Get from auth
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user