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:krow_data_connect/krow_data_connect.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
@@ -14,27 +15,36 @@ import '../../domain/repositories/personal_info_repository_interface.dart';
|
||||
/// - Containing no business logic
|
||||
class PersonalInfoRepositoryImpl implements PersonalInfoRepositoryInterface {
|
||||
final ExampleConnector _dataConnect;
|
||||
final FirebaseAuth _firebaseAuth;
|
||||
|
||||
/// Creates a [PersonalInfoRepositoryImpl].
|
||||
///
|
||||
/// Requires the Firebase Data Connect connector instance.
|
||||
/// Requires the Firebase Data Connect connector instance and Firebase Auth.
|
||||
PersonalInfoRepositoryImpl({
|
||||
required ExampleConnector dataConnect,
|
||||
}) : _dataConnect = dataConnect;
|
||||
required FirebaseAuth firebaseAuth,
|
||||
}) : _dataConnect = dataConnect,
|
||||
_firebaseAuth = firebaseAuth;
|
||||
|
||||
@override
|
||||
Future<Staff> getStaffProfile(String staffId) async {
|
||||
// Query staff data from Firebase Data Connect
|
||||
final QueryResult<GetStaffByIdData, GetStaffByIdVariables> result =
|
||||
await _dataConnect.getStaffById(id: staffId).execute();
|
||||
|
||||
final staff = result.data.staff;
|
||||
if (staff == null) {
|
||||
throw Exception('Staff profile not found for ID: $staffId');
|
||||
Future<Staff> getStaffProfile() async {
|
||||
final user = _firebaseAuth.currentUser;
|
||||
if (user == null) {
|
||||
throw Exception('User not authenticated');
|
||||
}
|
||||
|
||||
// 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
|
||||
return _mapToStaffEntity(staff);
|
||||
return _mapToStaffEntity(rawStaff);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -54,7 +64,7 @@ class PersonalInfoRepositoryImpl implements PersonalInfoRepositoryInterface {
|
||||
}
|
||||
|
||||
// Fetch the updated staff profile to return complete entity
|
||||
return getStaffProfile(staff.id);
|
||||
return getStaffProfile();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -69,16 +79,22 @@ class PersonalInfoRepositoryImpl implements PersonalInfoRepositoryInterface {
|
||||
/// Maps a data_connect Staff DTO to a domain Staff entity.
|
||||
///
|
||||
/// This mapping isolates the domain from data layer implementation details.
|
||||
Staff _mapToStaffEntity(GetStaffByIdStaff dto) {
|
||||
Staff _mapToStaffEntity(GetStaffByUserIdStaffs dto) {
|
||||
return Staff(
|
||||
id: dto.id,
|
||||
authProviderId: dto.userId,
|
||||
name: dto.fullName,
|
||||
email: dto.email ?? '',
|
||||
phone: dto.phone,
|
||||
status: StaffStatus.active, // TODO: Map from actual status field when available
|
||||
address: dto.addres,
|
||||
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
|
||||
/// the data_connect layer, following Clean Architecture principles.
|
||||
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.
|
||||
Future<Staff> getStaffProfile(String staffId);
|
||||
Future<Staff> getStaffProfile();
|
||||
|
||||
/// Updates the staff profile information.
|
||||
///
|
||||
|
||||
@@ -2,23 +2,12 @@ import 'package:krow_core/core.dart';
|
||||
import 'package:krow_domain/krow_domain.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.
|
||||
///
|
||||
/// This use case fetches the complete staff profile from the repository,
|
||||
/// which delegates to the data_connect layer for data access.
|
||||
class GetPersonalInfoUseCase
|
||||
implements UseCase<GetPersonalInfoArguments, Staff> {
|
||||
implements NoInputUseCase<Staff> {
|
||||
final PersonalInfoRepositoryInterface _repository;
|
||||
|
||||
/// Creates a [GetPersonalInfoUseCase].
|
||||
@@ -27,7 +16,7 @@ class GetPersonalInfoUseCase
|
||||
GetPersonalInfoUseCase(this._repository);
|
||||
|
||||
@override
|
||||
Future<Staff> call(GetPersonalInfoArguments arguments) {
|
||||
return _repository.getStaffProfile(arguments.staffId);
|
||||
Future<Staff> call() {
|
||||
return _repository.getStaffProfile();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,23 +2,12 @@ import 'package:krow_core/core.dart';
|
||||
import 'package:krow_domain/krow_domain.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.
|
||||
///
|
||||
/// This use case updates the staff profile information
|
||||
/// through the repository, which delegates to the data_connect layer.
|
||||
class UpdatePersonalInfoUseCase
|
||||
implements UseCase<UpdatePersonalInfoArguments, Staff> {
|
||||
implements UseCase<Staff, Staff> {
|
||||
final PersonalInfoRepositoryInterface _repository;
|
||||
|
||||
/// Creates an [UpdatePersonalInfoUseCase].
|
||||
@@ -27,7 +16,7 @@ class UpdatePersonalInfoUseCase
|
||||
UpdatePersonalInfoUseCase(this._repository);
|
||||
|
||||
@override
|
||||
Future<Staff> call(UpdatePersonalInfoArguments arguments) {
|
||||
return _repository.updateStaffProfile(arguments.staff);
|
||||
Future<Staff> call(Staff arguments) {
|
||||
return _repository.updateStaffProfile(arguments);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'package:krow_core/core.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
|
||||
import '../../domain/usecases/get_personal_info_usecase.dart';
|
||||
@@ -16,18 +17,15 @@ class PersonalInfoBloc extends Bloc<PersonalInfoEvent, PersonalInfoState>
|
||||
implements Disposable {
|
||||
final GetPersonalInfoUseCase _getPersonalInfoUseCase;
|
||||
final UpdatePersonalInfoUseCase _updatePersonalInfoUseCase;
|
||||
final String _staffId;
|
||||
|
||||
/// 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({
|
||||
required GetPersonalInfoUseCase getPersonalInfoUseCase,
|
||||
required UpdatePersonalInfoUseCase updatePersonalInfoUseCase,
|
||||
required String staffId,
|
||||
}) : _getPersonalInfoUseCase = getPersonalInfoUseCase,
|
||||
_updatePersonalInfoUseCase = updatePersonalInfoUseCase,
|
||||
_staffId = staffId,
|
||||
super(const PersonalInfoState()) {
|
||||
on<PersonalInfoLoadRequested>(_onLoadRequested);
|
||||
on<PersonalInfoFieldUpdated>(_onFieldUpdated);
|
||||
@@ -42,9 +40,7 @@ class PersonalInfoBloc extends Bloc<PersonalInfoEvent, PersonalInfoState>
|
||||
) async {
|
||||
emit(state.copyWith(status: PersonalInfoStatus.loading));
|
||||
try {
|
||||
final Staff staff = await _getPersonalInfoUseCase(
|
||||
GetPersonalInfoArguments(staffId: _staffId),
|
||||
);
|
||||
final Staff staff = await _getPersonalInfoUseCase();
|
||||
emit(state.copyWith(
|
||||
status: PersonalInfoStatus.loaded,
|
||||
staff: staff,
|
||||
@@ -115,7 +111,7 @@ class PersonalInfoBloc extends Bloc<PersonalInfoEvent, PersonalInfoState>
|
||||
emit(state.copyWith(status: PersonalInfoStatus.saving));
|
||||
try {
|
||||
final Staff updatedStaff = await _updatePersonalInfoUseCase(
|
||||
UpdatePersonalInfoArguments(staff: state.staff!),
|
||||
state.staff!,
|
||||
);
|
||||
emit(state.copyWith(
|
||||
status: PersonalInfoStatus.saved,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:flutter/material.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/usecases/get_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.
|
||||
///
|
||||
/// The module:
|
||||
/// - Registers repository implementations (mock for now, will use real impl later)
|
||||
/// - Registers repository implementations
|
||||
/// - Registers use cases that contain business logic
|
||||
/// - Registers BLoC for state management
|
||||
/// - Defines routes for navigation
|
||||
class StaffProfileInfoModule extends Module {
|
||||
@override
|
||||
void binds(Injector i) {
|
||||
// Repository - using mock for now
|
||||
// TODO: Replace with PersonalInfoRepositoryImpl when Firebase Data Connect is configured
|
||||
// Repository
|
||||
i.addLazySingleton<PersonalInfoRepositoryInterface>(
|
||||
PersonalInfoRepositoryMock.new,
|
||||
() => PersonalInfoRepositoryImpl(
|
||||
dataConnect: ExampleConnector.instance,
|
||||
firebaseAuth: FirebaseAuth.instance,
|
||||
),
|
||||
);
|
||||
|
||||
// Use Cases - delegate business logic to repository
|
||||
@@ -36,12 +40,10 @@ class StaffProfileInfoModule extends Module {
|
||||
);
|
||||
|
||||
// BLoC - manages presentation state
|
||||
// TODO: Get actual staffId from authentication state
|
||||
i.addLazySingleton<PersonalInfoBloc>(
|
||||
() => PersonalInfoBloc(
|
||||
getPersonalInfoUseCase: i.get<GetPersonalInfoUseCase>(),
|
||||
updatePersonalInfoUseCase: i.get<UpdatePersonalInfoUseCase>(),
|
||||
staffId: 'mock-staff-1', // TODO: Get from auth
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user