Enhance staff profile management by updating data structures and repository implementation
- Added new fields to the Staff entity for better profile details. - Updated ProfileRepositoryImpl to fetch staff profiles using a connector. - Refactored GetProfileUseCase to map staff data to UI entities. - Improved dependency injection in StaffProfileModule. - Cleaned up unused mock data references and streamlined profile page logic.
This commit is contained in:
@@ -29,17 +29,21 @@ enum StaffStatus {
|
|||||||
/// Contains all personal and professional details of a staff member.
|
/// Contains all personal and professional details of a staff member.
|
||||||
/// Linked to a [User] via [authProviderId].
|
/// Linked to a [User] via [authProviderId].
|
||||||
class Staff extends Equatable {
|
class Staff extends Equatable {
|
||||||
|
|
||||||
const Staff({
|
const Staff({
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.authProviderId,
|
required this.authProviderId,
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.email,
|
required this.email,
|
||||||
this.phone,
|
this.phone,
|
||||||
|
this.avatar,
|
||||||
required this.status,
|
required this.status,
|
||||||
this.address,
|
this.address,
|
||||||
this.avatar,
|
this.totalShifts,
|
||||||
this.livePhoto,
|
this.averageRating,
|
||||||
|
this.onTimeRate,
|
||||||
|
this.noShowCount,
|
||||||
|
this.cancellationCount,
|
||||||
|
this.reliabilityScore,
|
||||||
});
|
});
|
||||||
/// Unique identifier for the staff profile.
|
/// Unique identifier for the staff profile.
|
||||||
final String id;
|
final String id;
|
||||||
@@ -56,17 +60,34 @@ class Staff extends Equatable {
|
|||||||
/// Contact phone number.
|
/// Contact phone number.
|
||||||
final String? phone;
|
final String? phone;
|
||||||
|
|
||||||
|
/// Profile picture URL.
|
||||||
|
final String? avatar;
|
||||||
|
|
||||||
/// Current workflow status of the staff member.
|
/// Current workflow status of the staff member.
|
||||||
final StaffStatus status;
|
final StaffStatus status;
|
||||||
|
|
||||||
/// Physical address string.
|
/// The user's physical address.
|
||||||
|
///
|
||||||
|
/// Can be used for location-based job matching.
|
||||||
final String? address;
|
final String? address;
|
||||||
|
|
||||||
/// URL to the avatar image.
|
/// The total number of shifts completed.
|
||||||
final String? avatar;
|
final int? totalShifts;
|
||||||
|
|
||||||
/// URL to a verified live photo for identity verification.
|
/// The average rating from businesses.
|
||||||
final String? livePhoto;
|
final double? averageRating;
|
||||||
|
|
||||||
|
/// The percentage of shifts arrived on time.
|
||||||
|
final int? onTimeRate;
|
||||||
|
|
||||||
|
/// The number of no-shows.
|
||||||
|
final int? noShowCount;
|
||||||
|
|
||||||
|
/// The number of cancellations within 24h.
|
||||||
|
final int? cancellationCount;
|
||||||
|
|
||||||
|
/// The reliability score (0-100).
|
||||||
|
final int? reliabilityScore;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => <Object?>[
|
List<Object?> get props => <Object?>[
|
||||||
@@ -75,9 +96,14 @@ class Staff extends Equatable {
|
|||||||
name,
|
name,
|
||||||
email,
|
email,
|
||||||
phone,
|
phone,
|
||||||
|
avatar,
|
||||||
status,
|
status,
|
||||||
address,
|
address,
|
||||||
avatar,
|
totalShifts,
|
||||||
livePhoto,
|
averageRating,
|
||||||
|
onTimeRate,
|
||||||
|
noShowCount,
|
||||||
|
cancellationCount,
|
||||||
|
reliabilityScore,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class ClientCreateOrderModule extends Module {
|
|||||||
i.addLazySingleton(CreateRapidOrderUseCase.new);
|
i.addLazySingleton(CreateRapidOrderUseCase.new);
|
||||||
|
|
||||||
// BLoCs
|
// BLoCs
|
||||||
i.addSingleton<ClientCreateOrderBloc>(ClientCreateOrderBloc.new);
|
i.add<ClientCreateOrderBloc>(ClientCreateOrderBloc.new);
|
||||||
i.add<RapidOrderBloc>(RapidOrderBloc.new);
|
i.add<RapidOrderBloc>(RapidOrderBloc.new);
|
||||||
i.add<OneTimeOrderBloc>(
|
i.add<OneTimeOrderBloc>(
|
||||||
() => OneTimeOrderBloc(
|
() => OneTimeOrderBloc(
|
||||||
|
|||||||
@@ -14,22 +14,49 @@ import '../../domain/repositories/profile_repository.dart';
|
|||||||
/// Currently uses [ProfileRepositoryMock] from data_connect.
|
/// Currently uses [ProfileRepositoryMock] from data_connect.
|
||||||
/// When Firebase Data Connect is ready, this will be swapped with a real implementation.
|
/// When Firebase Data Connect is ready, this will be swapped with a real implementation.
|
||||||
class ProfileRepositoryImpl implements ProfileRepositoryInterface {
|
class ProfileRepositoryImpl implements ProfileRepositoryInterface {
|
||||||
final ProfileRepositoryMock _dataConnectRepository;
|
|
||||||
|
|
||||||
/// Creates a [ProfileRepositoryImpl].
|
/// Creates a [ProfileRepositoryImpl].
|
||||||
///
|
///
|
||||||
/// Requires a [ProfileRepositoryMock] from the data_connect package.
|
/// Requires a [ExampleConnector] from the data_connect package.
|
||||||
const ProfileRepositoryImpl(this._dataConnectRepository);
|
const ProfileRepositoryImpl({required this.connector});
|
||||||
|
|
||||||
|
/// The Data Connect connector used for data operations.
|
||||||
|
final ExampleConnector connector;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Staff> getStaffProfile(String userId) {
|
Future<Staff> getStaffProfile(String userId) async {
|
||||||
// Delegate directly to data_connect - no business logic here
|
// ignore: always_specify_types
|
||||||
return _dataConnectRepository.getStaffProfile(userId);
|
final response = await connector.getStaffByUserId(userId: userId).execute();
|
||||||
|
|
||||||
|
if (response.data.staffs.isEmpty) {
|
||||||
|
// TODO: Handle user not found properly with domain exception
|
||||||
|
throw Exception('Staff not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
final GetStaffByUserIdStaffs rawStaff = response.data.staffs.first;
|
||||||
|
|
||||||
|
// Map the raw data connect object to the Domain Entity
|
||||||
|
return Staff(
|
||||||
|
id: rawStaff.id,
|
||||||
|
authProviderId: rawStaff.userId,
|
||||||
|
name: rawStaff.fullName,
|
||||||
|
email: rawStaff.email ?? '',
|
||||||
|
phone: rawStaff.phone,
|
||||||
|
avatar: rawStaff.photoUrl,
|
||||||
|
status: StaffStatus.active,
|
||||||
|
address: null,
|
||||||
|
totalShifts: rawStaff.totalShifts,
|
||||||
|
averageRating: rawStaff.averageRating,
|
||||||
|
onTimeRate: rawStaff.onTimeRate,
|
||||||
|
noShowCount: rawStaff.noShowCount,
|
||||||
|
cancellationCount: rawStaff.cancellationCount,
|
||||||
|
reliabilityScore: rawStaff.reliabilityScore,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> signOut() {
|
Future<void> signOut() {
|
||||||
// Delegate directly to data_connect - no business logic here
|
// TODO: Implement sign out via Auth interface, not profile repository
|
||||||
return _dataConnectRepository.signOut();
|
// For now, no-op or delegate if connector has auth
|
||||||
|
return Future.value();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,19 +31,18 @@ class GetProfileUseCase implements UseCase<String, StaffProfileUI> {
|
|||||||
final staff = await _repository.getStaffProfile(userId);
|
final staff = await _repository.getStaffProfile(userId);
|
||||||
|
|
||||||
// Map to UI entity with additional profile data
|
// Map to UI entity with additional profile data
|
||||||
// TODO: Replace mock data with actual profile statistics from backend
|
|
||||||
return StaffProfileUI(
|
return StaffProfileUI(
|
||||||
staff: staff,
|
staff: staff,
|
||||||
totalShifts: 0,
|
totalShifts: staff.totalShifts ?? 0,
|
||||||
averageRating: 5.0,
|
averageRating: staff.averageRating ?? 5.0,
|
||||||
onTimeRate: 100,
|
onTimeRate: staff.onTimeRate ?? 0,
|
||||||
noShowCount: 0,
|
noShowCount: staff.noShowCount ?? 0,
|
||||||
cancellationCount: 0,
|
cancellationCount: staff.cancellationCount ?? 0,
|
||||||
reliabilityScore: 100,
|
reliabilityScore: staff.reliabilityScore ?? 100,
|
||||||
hasPersonalInfo: staff.phone != null && staff.phone!.isNotEmpty,
|
hasPersonalInfo: staff.phone != null && staff.phone!.isNotEmpty,
|
||||||
hasEmergencyContact: false, // TODO: Fetch from backend
|
hasEmergencyContact: false, // TODO: Fetch from backend
|
||||||
hasExperience: false, // TODO: Fetch from backend
|
hasExperience: false, // TODO: Fetch from backend
|
||||||
hasAttire: staff.avatar != null,
|
hasAttire: false, // TODO: Check attire items from backend when available
|
||||||
hasDocuments: false, // TODO: Fetch from backend
|
hasDocuments: false, // TODO: Fetch from backend
|
||||||
hasCertificates: false, // TODO: Fetch from backend
|
hasCertificates: false, // TODO: Fetch from backend
|
||||||
hasTaxForms: false, // TODO: Fetch from backend
|
hasTaxForms: false, // TODO: Fetch from backend
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ class StaffProfilePage extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final i18n = t.staff.profile;
|
final TranslationsStaffProfileEn i18n = t.staff.profile;
|
||||||
final cubit = Modular.get<ProfileCubit>();
|
final ProfileCubit cubit = Modular.get<ProfileCubit>();
|
||||||
|
|
||||||
// Load profile data on first build
|
// Load profile data on first build
|
||||||
// TODO: Get actual userId from auth session
|
// TODO: Get actual userId from auth session
|
||||||
@@ -40,7 +40,6 @@ class StaffProfilePage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: UiColors.background,
|
|
||||||
body: BlocBuilder<ProfileCubit, ProfileState>(
|
body: BlocBuilder<ProfileCubit, ProfileState>(
|
||||||
bloc: cubit,
|
bloc: cubit,
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
|
|||||||
@@ -15,19 +15,15 @@ import 'presentation/pages/staff_profile_page.dart';
|
|||||||
/// following Clean Architecture principles.
|
/// following Clean Architecture principles.
|
||||||
///
|
///
|
||||||
/// Dependency flow:
|
/// Dependency flow:
|
||||||
/// - Data source (ProfileRepositoryMock) from data_connect package
|
|
||||||
/// - Repository implementation (ProfileRepositoryImpl) delegates to data_connect
|
/// - Repository implementation (ProfileRepositoryImpl) delegates to data_connect
|
||||||
/// - Use cases depend on repository interface
|
/// - Use cases depend on repository interface
|
||||||
/// - Cubit depends on use cases
|
/// - Cubit depends on use cases
|
||||||
class StaffProfileModule extends Module {
|
class StaffProfileModule extends Module {
|
||||||
@override
|
@override
|
||||||
void binds(Injector i) {
|
void binds(Injector i) {
|
||||||
// Data layer - Get mock from data_connect package
|
|
||||||
i.addLazySingleton<ProfileRepositoryMock>(ProfileRepositoryMock.new);
|
|
||||||
|
|
||||||
// Repository implementation - delegates to data_connect
|
// Repository implementation - delegates to data_connect
|
||||||
i.addLazySingleton<ProfileRepositoryInterface>(
|
i.addLazySingleton<ProfileRepositoryInterface>(
|
||||||
() => ProfileRepositoryImpl(i.get<ProfileRepositoryMock>()),
|
() => ProfileRepositoryImpl(connector: ExampleConnector.instance),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Use cases - depend on repository interface
|
// Use cases - depend on repository interface
|
||||||
@@ -39,8 +35,7 @@ class StaffProfileModule extends Module {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Presentation layer - Cubit depends on use cases
|
// Presentation layer - Cubit depends on use cases
|
||||||
// Use addLazySingleton to create a new instance per module lifecycle
|
i.add<ProfileCubit>(
|
||||||
i.addLazySingleton(
|
|
||||||
() => ProfileCubit(
|
() => ProfileCubit(
|
||||||
i.get<GetProfileUseCase>(),
|
i.get<GetProfileUseCase>(),
|
||||||
i.get<SignOutUseCase>(),
|
i.get<SignOutUseCase>(),
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
/// Export the modular feature definition.
|
|
||||||
export 'src/staff_profile_module.dart';
|
export 'src/staff_profile_module.dart';
|
||||||
|
|||||||
Reference in New Issue
Block a user