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:
Achintha Isuru
2026-01-26 18:03:16 -05:00
parent 43922ab014
commit f4ac292c14
7 changed files with 85 additions and 40 deletions

View File

@@ -29,17 +29,21 @@ enum StaffStatus {
/// Contains all personal and professional details of a staff member.
/// Linked to a [User] via [authProviderId].
class Staff extends Equatable {
const Staff({
required this.id,
required this.authProviderId,
required this.name,
required this.email,
this.phone,
this.avatar,
required this.status,
this.address,
this.avatar,
this.livePhoto,
this.totalShifts,
this.averageRating,
this.onTimeRate,
this.noShowCount,
this.cancellationCount,
this.reliabilityScore,
});
/// Unique identifier for the staff profile.
final String id;
@@ -56,17 +60,34 @@ class Staff extends Equatable {
/// Contact phone number.
final String? phone;
/// Profile picture URL.
final String? avatar;
/// Current workflow status of the staff member.
final StaffStatus status;
/// Physical address string.
/// The user's physical address.
///
/// Can be used for location-based job matching.
final String? address;
/// URL to the avatar image.
final String? avatar;
/// The total number of shifts completed.
final int? totalShifts;
/// URL to a verified live photo for identity verification.
final String? livePhoto;
/// The average rating from businesses.
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
List<Object?> get props => <Object?>[
@@ -75,9 +96,14 @@ class Staff extends Equatable {
name,
email,
phone,
avatar,
status,
address,
avatar,
livePhoto,
totalShifts,
averageRating,
onTimeRate,
noShowCount,
cancellationCount,
reliabilityScore,
];
}
}

View File

@@ -41,7 +41,7 @@ class ClientCreateOrderModule extends Module {
i.addLazySingleton(CreateRapidOrderUseCase.new);
// BLoCs
i.addSingleton<ClientCreateOrderBloc>(ClientCreateOrderBloc.new);
i.add<ClientCreateOrderBloc>(ClientCreateOrderBloc.new);
i.add<RapidOrderBloc>(RapidOrderBloc.new);
i.add<OneTimeOrderBloc>(
() => OneTimeOrderBloc(

View File

@@ -14,22 +14,49 @@ import '../../domain/repositories/profile_repository.dart';
/// Currently uses [ProfileRepositoryMock] from data_connect.
/// When Firebase Data Connect is ready, this will be swapped with a real implementation.
class ProfileRepositoryImpl implements ProfileRepositoryInterface {
final ProfileRepositoryMock _dataConnectRepository;
/// Creates a [ProfileRepositoryImpl].
///
/// Requires a [ProfileRepositoryMock] from the data_connect package.
const ProfileRepositoryImpl(this._dataConnectRepository);
/// Requires a [ExampleConnector] from the data_connect package.
const ProfileRepositoryImpl({required this.connector});
/// The Data Connect connector used for data operations.
final ExampleConnector connector;
@override
Future<Staff> getStaffProfile(String userId) {
// Delegate directly to data_connect - no business logic here
return _dataConnectRepository.getStaffProfile(userId);
Future<Staff> getStaffProfile(String userId) async {
// ignore: always_specify_types
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
Future<void> signOut() {
// Delegate directly to data_connect - no business logic here
return _dataConnectRepository.signOut();
// TODO: Implement sign out via Auth interface, not profile repository
// For now, no-op or delegate if connector has auth
return Future.value();
}
}

View File

@@ -31,19 +31,18 @@ class GetProfileUseCase implements UseCase<String, StaffProfileUI> {
final staff = await _repository.getStaffProfile(userId);
// Map to UI entity with additional profile data
// TODO: Replace mock data with actual profile statistics from backend
return StaffProfileUI(
staff: staff,
totalShifts: 0,
averageRating: 5.0,
onTimeRate: 100,
noShowCount: 0,
cancellationCount: 0,
reliabilityScore: 100,
totalShifts: staff.totalShifts ?? 0,
averageRating: staff.averageRating ?? 5.0,
onTimeRate: staff.onTimeRate ?? 0,
noShowCount: staff.noShowCount ?? 0,
cancellationCount: staff.cancellationCount ?? 0,
reliabilityScore: staff.reliabilityScore ?? 100,
hasPersonalInfo: staff.phone != null && staff.phone!.isNotEmpty,
hasEmergencyContact: 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
hasCertificates: false, // TODO: Fetch from backend
hasTaxForms: false, // TODO: Fetch from backend

View File

@@ -28,8 +28,8 @@ class StaffProfilePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final i18n = t.staff.profile;
final cubit = Modular.get<ProfileCubit>();
final TranslationsStaffProfileEn i18n = t.staff.profile;
final ProfileCubit cubit = Modular.get<ProfileCubit>();
// Load profile data on first build
// TODO: Get actual userId from auth session
@@ -40,7 +40,6 @@ class StaffProfilePage extends StatelessWidget {
}
return Scaffold(
backgroundColor: UiColors.background,
body: BlocBuilder<ProfileCubit, ProfileState>(
bloc: cubit,
builder: (context, state) {

View File

@@ -15,19 +15,15 @@ import 'presentation/pages/staff_profile_page.dart';
/// following Clean Architecture principles.
///
/// Dependency flow:
/// - Data source (ProfileRepositoryMock) from data_connect package
/// - Repository implementation (ProfileRepositoryImpl) delegates to data_connect
/// - Use cases depend on repository interface
/// - Cubit depends on use cases
class StaffProfileModule extends Module {
@override
void binds(Injector i) {
// Data layer - Get mock from data_connect package
i.addLazySingleton<ProfileRepositoryMock>(ProfileRepositoryMock.new);
// Repository implementation - delegates to data_connect
i.addLazySingleton<ProfileRepositoryInterface>(
() => ProfileRepositoryImpl(i.get<ProfileRepositoryMock>()),
() => ProfileRepositoryImpl(connector: ExampleConnector.instance),
);
// Use cases - depend on repository interface
@@ -39,8 +35,7 @@ class StaffProfileModule extends Module {
);
// Presentation layer - Cubit depends on use cases
// Use addLazySingleton to create a new instance per module lifecycle
i.addLazySingleton(
i.add<ProfileCubit>(
() => ProfileCubit(
i.get<GetProfileUseCase>(),
i.get<SignOutUseCase>(),

View File

@@ -1,2 +1 @@
/// Export the modular feature definition.
export 'src/staff_profile_module.dart';