feat: Refactor context reading in emergency contact and FAQs widgets

- Updated the context reading method in `EmergencyContactAddButton` and `EmergencyContactFormItem` to use `ReadContext`.
- Modified the `FaqsWidget` to utilize `ReadContext` for fetching FAQs.
- Adjusted the `PrivacySectionWidget` to read from `PrivacySecurityBloc` using `ReadContext`.

feat: Implement Firebase Auth isolation pattern

- Introduced `FirebaseAuthService` and `FirebaseAuthServiceImpl` to abstract Firebase Auth operations.
- Ensured features do not directly import `firebase_auth`, adhering to architecture rules.

feat: Create repository interfaces for billing and coverage

- Added `BillingRepositoryInterface` for billing-related operations.
- Created `CoverageRepositoryInterface` for coverage data access.

feat: Add use cases for order management

- Implemented use cases for fetching hubs, managers, and roles related to orders.
- Created `GetHubsUseCase`, `GetManagersByHubUseCase`, and `GetRolesByVendorUseCase`.

feat: Develop report use cases for client reports

- Added use cases for fetching various reports including coverage, daily operations, forecast, no-show, performance, and spend reports.
- Implemented `GetCoverageReportUseCase`, `GetDailyOpsReportUseCase`, `GetForecastReportUseCase`, `GetNoShowReportUseCase`, `GetPerformanceReportUseCase`, and `GetSpendReportUseCase`.

feat: Establish profile repository and use cases

- Created `ProfileRepositoryInterface` for staff profile data access.
- Implemented use cases for retrieving staff profile and section statuses: `GetStaffProfileUseCase` and `GetProfileSectionsUseCase`.
- Added `SignOutUseCase` for signing out the current user.
This commit is contained in:
Achintha Isuru
2026-03-19 01:10:27 -04:00
parent a45a3f6af1
commit 843eec5692
123 changed files with 2102 additions and 1087 deletions

View File

@@ -1,17 +1,19 @@
import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart';
import 'package:staff_profile/src/domain/repositories/profile_repository_interface.dart';
/// Repository implementation for the main profile page.
///
/// Uses the V2 API to fetch staff profile, section statuses, and completion.
class ProfileRepositoryImpl {
class ProfileRepositoryImpl implements ProfileRepositoryInterface {
/// Creates a [ProfileRepositoryImpl].
ProfileRepositoryImpl({required BaseApiService apiService})
: _api = apiService;
final BaseApiService _api;
/// Fetches the staff profile from the V2 session endpoint.
@override
Future<Staff> getStaffProfile() async {
final ApiResponse response =
await _api.get(StaffEndpoints.session);
@@ -20,7 +22,7 @@ class ProfileRepositoryImpl {
return Staff.fromJson(json);
}
/// Fetches the profile section completion statuses.
@override
Future<ProfileSectionStatus> getProfileSections() async {
final ApiResponse response =
await _api.get(StaffEndpoints.profileSections);
@@ -29,7 +31,7 @@ class ProfileRepositoryImpl {
return ProfileSectionStatus.fromJson(json);
}
/// Signs out the current user.
@override
Future<void> signOut() async {
await _api.post(AuthEndpoints.signOut);
}

View File

@@ -0,0 +1,16 @@
import 'package:krow_domain/krow_domain.dart';
/// Abstract interface for the staff profile repository.
///
/// Defines the contract for fetching staff profile data,
/// section completion statuses, and signing out.
abstract interface class ProfileRepositoryInterface {
/// Fetches the staff profile from the backend.
Future<Staff> getStaffProfile();
/// Fetches the profile section completion statuses.
Future<ProfileSectionStatus> getProfileSections();
/// Signs out the current user.
Future<void> signOut();
}

View File

@@ -0,0 +1,17 @@
import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart';
import 'package:staff_profile/src/domain/repositories/profile_repository_interface.dart';
/// Use case for retrieving profile section completion statuses.
class GetProfileSectionsUseCase implements NoInputUseCase<ProfileSectionStatus> {
/// Creates a [GetProfileSectionsUseCase] with the required [repository].
GetProfileSectionsUseCase(this._repository);
final ProfileRepositoryInterface _repository;
@override
Future<ProfileSectionStatus> call() {
return _repository.getProfileSections();
}
}

View File

@@ -0,0 +1,17 @@
import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart';
import 'package:staff_profile/src/domain/repositories/profile_repository_interface.dart';
/// Use case for retrieving the staff member's profile.
class GetStaffProfileUseCase implements NoInputUseCase<Staff> {
/// Creates a [GetStaffProfileUseCase] with the required [repository].
GetStaffProfileUseCase(this._repository);
final ProfileRepositoryInterface _repository;
@override
Future<Staff> call() {
return _repository.getStaffProfile();
}
}

View File

@@ -0,0 +1,16 @@
import 'package:krow_core/core.dart';
import 'package:staff_profile/src/domain/repositories/profile_repository_interface.dart';
/// Use case for signing out the current user.
class SignOutUseCase implements NoInputUseCase<void> {
/// Creates a [SignOutUseCase] with the required [repository].
SignOutUseCase(this._repository);
final ProfileRepositoryInterface _repository;
@override
Future<void> call() {
return _repository.signOut();
}
}

View File

@@ -2,19 +2,30 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart';
import 'package:staff_profile/src/data/repositories/profile_repository_impl.dart';
import 'package:staff_profile/src/domain/usecases/get_profile_sections_usecase.dart';
import 'package:staff_profile/src/domain/usecases/get_staff_profile_usecase.dart';
import 'package:staff_profile/src/domain/usecases/sign_out_usecase.dart';
import 'package:staff_profile/src/presentation/blocs/profile_state.dart';
/// Cubit for managing the Profile feature state.
///
/// Uses the V2 API via [ProfileRepositoryImpl] for all data fetching.
/// Delegates all data fetching to use cases, following Clean Architecture.
/// Loads the staff profile and section completion statuses in a single flow.
class ProfileCubit extends Cubit<ProfileState>
with BlocErrorHandler<ProfileState> {
/// Creates a [ProfileCubit] with the required repository.
ProfileCubit(this._repository) : super(const ProfileState());
/// Creates a [ProfileCubit] with the required use cases.
ProfileCubit({
required GetStaffProfileUseCase getStaffProfileUseCase,
required GetProfileSectionsUseCase getProfileSectionsUseCase,
required SignOutUseCase signOutUseCase,
}) : _getStaffProfileUseCase = getStaffProfileUseCase,
_getProfileSectionsUseCase = getProfileSectionsUseCase,
_signOutUseCase = signOutUseCase,
super(const ProfileState());
final ProfileRepositoryImpl _repository;
final GetStaffProfileUseCase _getStaffProfileUseCase;
final GetProfileSectionsUseCase _getProfileSectionsUseCase;
final SignOutUseCase _signOutUseCase;
/// Loads the staff member's profile.
Future<void> loadProfile() async {
@@ -23,7 +34,7 @@ class ProfileCubit extends Cubit<ProfileState>
await handleError(
emit: emit,
action: () async {
final Staff profile = await _repository.getStaffProfile();
final Staff profile = await _getStaffProfileUseCase();
emit(state.copyWith(status: ProfileStatus.loaded, profile: profile));
},
onError: (String errorKey) =>
@@ -37,7 +48,7 @@ class ProfileCubit extends Cubit<ProfileState>
emit: emit,
action: () async {
final ProfileSectionStatus sections =
await _repository.getProfileSections();
await _getProfileSectionsUseCase();
emit(state.copyWith(
personalInfoComplete: sections.personalInfoCompleted,
emergencyContactsComplete: sections.emergencyContactCompleted,
@@ -62,7 +73,7 @@ class ProfileCubit extends Cubit<ProfileState>
await handleError(
emit: emit,
action: () async {
await _repository.signOut();
await _signOutUseCase();
emit(state.copyWith(status: ProfileStatus.signedOut));
},
onError: (String _) =>

View File

@@ -19,7 +19,7 @@ class LogoutButton extends StatelessWidget {
/// sign-out process via the ProfileCubit.
void _handleSignOut(BuildContext context, ProfileState state) {
if (state.status != ProfileStatus.loading) {
context.read<ProfileCubit>().signOut();
ReadContext(context).read<ProfileCubit>().signOut();
}
}
@@ -47,7 +47,7 @@ class LogoutButton extends StatelessWidget {
onTap: () {
_handleSignOut(
context,
context.read<ProfileCubit>().state,
ReadContext(context).read<ProfileCubit>().state,
);
},
borderRadius: UiConstants.radiusLg,

View File

@@ -4,13 +4,17 @@ import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart';
import 'package:staff_profile/src/data/repositories/profile_repository_impl.dart';
import 'package:staff_profile/src/domain/repositories/profile_repository_interface.dart';
import 'package:staff_profile/src/domain/usecases/get_profile_sections_usecase.dart';
import 'package:staff_profile/src/domain/usecases/get_staff_profile_usecase.dart';
import 'package:staff_profile/src/domain/usecases/sign_out_usecase.dart';
import 'package:staff_profile/src/presentation/blocs/profile_cubit.dart';
import 'package:staff_profile/src/presentation/pages/staff_profile_page.dart';
/// The entry module for the Staff Profile feature.
///
/// Uses the V2 REST API via [BaseApiService] for all backend access.
/// Section completion statuses are fetched in a single API call.
/// Registers repository interface, use cases, and cubit for DI.
class StaffProfileModule extends Module {
@override
List<Module> get imports => <Module>[CoreModule()];
@@ -18,15 +22,36 @@ class StaffProfileModule extends Module {
@override
void binds(Injector i) {
// Repository
i.addLazySingleton<ProfileRepositoryImpl>(
i.addLazySingleton<ProfileRepositoryInterface>(
() => ProfileRepositoryImpl(
apiService: i.get<BaseApiService>(),
),
);
// Use Cases
i.addLazySingleton<GetStaffProfileUseCase>(
() => GetStaffProfileUseCase(
i.get<ProfileRepositoryInterface>(),
),
);
i.addLazySingleton<GetProfileSectionsUseCase>(
() => GetProfileSectionsUseCase(
i.get<ProfileRepositoryInterface>(),
),
);
i.addLazySingleton<SignOutUseCase>(
() => SignOutUseCase(
i.get<ProfileRepositoryInterface>(),
),
);
// Cubit
i.addLazySingleton<ProfileCubit>(
() => ProfileCubit(i.get<ProfileRepositoryImpl>()),
() => ProfileCubit(
getStaffProfileUseCase: i.get<GetStaffProfileUseCase>(),
getProfileSectionsUseCase: i.get<GetProfileSectionsUseCase>(),
signOutUseCase: i.get<SignOutUseCase>(),
),
);
}