Add staff reliability stats & shift location

Introduce staff reliability statistics and location fields across domain, data, and UI. Changes include:

- New API endpoint StaffEndpoints.profileStats ('/staff/profile/stats').
- New domain entity StaffReliabilityStats with JSON (de)serialization and export.
- Profile repository: getReliabilityStats implementation and interface addition.
- New GetReliabilityStatsUseCase and DI registration in StaffProfileModule.
- ProfileCubit/state: load and store reliabilityStats; UI wired to display ReliabilityStatsCard and ReliabilityScoreBar using state values.
- Coverage/shift updates: Added AssignedWorker.hasReview to track if a worker was reviewed; added locationName/locationAddress to ShiftWithWorkers and show location in ShiftHeader; hide rate button if worker.hasReview.
- Clock-in handling: treat backend ALREADY_CLOCKED_IN (409) as idempotent by re-fetching attendance and emitting success when appropriate.

These changes wire backend stats through repository/usecase/cubit to the profile UI and add shift location and review-awareness to client views.
This commit is contained in:
Achintha Isuru
2026-03-19 10:53:37 -04:00
parent 88a319da4f
commit 493891eea0
15 changed files with 247 additions and 37 deletions

View File

@@ -14,6 +14,10 @@ abstract final class StaffEndpoints {
static const ApiEndpoint profileCompletion = static const ApiEndpoint profileCompletion =
ApiEndpoint('/staff/profile-completion'); ApiEndpoint('/staff/profile-completion');
/// Staff reliability and performance statistics.
static const ApiEndpoint profileStats =
ApiEndpoint('/staff/profile/stats');
/// Staff availability schedule. /// Staff availability schedule.
static const ApiEndpoint availability = ApiEndpoint('/staff/availability'); static const ApiEndpoint availability = ApiEndpoint('/staff/availability');

View File

@@ -99,6 +99,7 @@ export 'src/entities/profile/accessibility.dart';
// Ratings // Ratings
export 'src/entities/ratings/staff_rating.dart'; export 'src/entities/ratings/staff_rating.dart';
export 'src/entities/ratings/staff_reliability_stats.dart';
// Home // Home
export 'src/entities/home/client_dashboard.dart'; export 'src/entities/home/client_dashboard.dart';

View File

@@ -13,6 +13,7 @@ class AssignedWorker extends Equatable {
required this.fullName, required this.fullName,
required this.status, required this.status,
this.checkInAt, this.checkInAt,
this.hasReview = false,
}); });
/// Deserialises an [AssignedWorker] from a V2 API JSON map. /// Deserialises an [AssignedWorker] from a V2 API JSON map.
@@ -25,6 +26,7 @@ class AssignedWorker extends Equatable {
checkInAt: json['checkInAt'] != null checkInAt: json['checkInAt'] != null
? DateTime.parse(json['checkInAt'] as String) ? DateTime.parse(json['checkInAt'] as String)
: null, : null,
hasReview: json['hasReview'] as bool? ?? false,
); );
} }
@@ -43,6 +45,9 @@ class AssignedWorker extends Equatable {
/// When the worker clocked in (null if not yet). /// When the worker clocked in (null if not yet).
final DateTime? checkInAt; final DateTime? checkInAt;
/// Whether this worker has already been reviewed for this assignment.
final bool hasReview;
/// Serialises this [AssignedWorker] to a JSON map. /// Serialises this [AssignedWorker] to a JSON map.
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return <String, dynamic>{ return <String, dynamic>{
@@ -51,6 +56,7 @@ class AssignedWorker extends Equatable {
'fullName': fullName, 'fullName': fullName,
'status': status.toJson(), 'status': status.toJson(),
'checkInAt': checkInAt?.toIso8601String(), 'checkInAt': checkInAt?.toIso8601String(),
'hasReview': hasReview,
}; };
} }
@@ -61,5 +67,6 @@ class AssignedWorker extends Equatable {
fullName, fullName,
status, status,
checkInAt, checkInAt,
hasReview,
]; ];
} }

View File

@@ -15,6 +15,8 @@ class ShiftWithWorkers extends Equatable {
required this.requiredWorkerCount, required this.requiredWorkerCount,
required this.assignedWorkerCount, required this.assignedWorkerCount,
this.assignedWorkers = const <AssignedWorker>[], this.assignedWorkers = const <AssignedWorker>[],
this.locationName = '',
this.locationAddress = '',
}); });
/// Deserialises a [ShiftWithWorkers] from a V2 API JSON map. /// Deserialises a [ShiftWithWorkers] from a V2 API JSON map.
@@ -30,6 +32,8 @@ class ShiftWithWorkers extends Equatable {
return ShiftWithWorkers( return ShiftWithWorkers(
shiftId: json['shiftId'] as String, shiftId: json['shiftId'] as String,
roleName: json['roleName'] as String? ?? '', roleName: json['roleName'] as String? ?? '',
locationName: json['locationName'] as String? ?? '',
locationAddress: json['locationAddress'] as String? ?? '',
timeRange: TimeRange.fromJson(json['timeRange'] as Map<String, dynamic>), timeRange: TimeRange.fromJson(json['timeRange'] as Map<String, dynamic>),
requiredWorkerCount: (json['requiredWorkerCount'] as num).toInt(), requiredWorkerCount: (json['requiredWorkerCount'] as num).toInt(),
assignedWorkerCount: (json['assignedWorkerCount'] as num).toInt(), assignedWorkerCount: (json['assignedWorkerCount'] as num).toInt(),
@@ -55,11 +59,19 @@ class ShiftWithWorkers extends Equatable {
/// List of assigned workers with their statuses. /// List of assigned workers with their statuses.
final List<AssignedWorker> assignedWorkers; final List<AssignedWorker> assignedWorkers;
/// Location or hub name for this shift.
final String locationName;
/// Street address for this shift.
final String locationAddress;
/// Serialises this [ShiftWithWorkers] to a JSON map. /// Serialises this [ShiftWithWorkers] to a JSON map.
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return <String, dynamic>{ return <String, dynamic>{
'shiftId': shiftId, 'shiftId': shiftId,
'roleName': roleName, 'roleName': roleName,
'locationName': locationName,
'locationAddress': locationAddress,
'timeRange': timeRange.toJson(), 'timeRange': timeRange.toJson(),
'requiredWorkerCount': requiredWorkerCount, 'requiredWorkerCount': requiredWorkerCount,
'assignedWorkerCount': assignedWorkerCount, 'assignedWorkerCount': assignedWorkerCount,
@@ -76,5 +88,7 @@ class ShiftWithWorkers extends Equatable {
requiredWorkerCount, requiredWorkerCount,
assignedWorkerCount, assignedWorkerCount,
assignedWorkers, assignedWorkers,
locationName,
locationAddress,
]; ];
} }

View File

@@ -0,0 +1,84 @@
import 'package:equatable/equatable.dart';
/// Aggregated reliability and performance statistics for a staff member.
///
/// Returned by `GET /staff/profile/stats`.
class StaffReliabilityStats extends Equatable {
/// Creates a [StaffReliabilityStats] instance.
const StaffReliabilityStats({
required this.staffId,
this.totalShifts = 0,
this.averageRating = 0,
this.ratingCount = 0,
this.onTimeRate = 0,
this.noShowCount = 0,
this.cancellationCount = 0,
this.reliabilityScore = 0,
});
/// Deserialises from a V2 API JSON map.
factory StaffReliabilityStats.fromJson(Map<String, dynamic> json) {
return StaffReliabilityStats(
staffId: json['staffId'] as String,
totalShifts: (json['totalShifts'] as num?)?.toInt() ?? 0,
averageRating: (json['averageRating'] as num?)?.toDouble() ?? 0,
ratingCount: (json['ratingCount'] as num?)?.toInt() ?? 0,
onTimeRate: (json['onTimeRate'] as num?)?.toDouble() ?? 0,
noShowCount: (json['noShowCount'] as num?)?.toInt() ?? 0,
cancellationCount: (json['cancellationCount'] as num?)?.toInt() ?? 0,
reliabilityScore: (json['reliabilityScore'] as num?)?.toDouble() ?? 0,
);
}
/// The staff member's unique identifier.
final String staffId;
/// Total completed shifts.
final int totalShifts;
/// Average rating from client reviews (0-5).
final double averageRating;
/// Number of ratings received.
final int ratingCount;
/// Percentage of shifts clocked in on time (0-100).
final double onTimeRate;
/// Number of no-show incidents.
final int noShowCount;
/// Number of worker-initiated cancellations.
final int cancellationCount;
/// Composite reliability score (0-100).
///
/// Weighted: 45% on-time rate + 35% completion rate + 20% rating score.
final double reliabilityScore;
/// Serialises to a JSON map.
Map<String, dynamic> toJson() {
return <String, dynamic>{
'staffId': staffId,
'totalShifts': totalShifts,
'averageRating': averageRating,
'ratingCount': ratingCount,
'onTimeRate': onTimeRate,
'noShowCount': noShowCount,
'cancellationCount': cancellationCount,
'reliabilityScore': reliabilityScore,
};
}
@override
List<Object?> get props => <Object?>[
staffId,
totalShifts,
averageRating,
ratingCount,
onTimeRate,
noShowCount,
cancellationCount,
reliabilityScore,
];
}

View File

@@ -161,6 +161,7 @@ class _CoverageShiftListState extends State<CoverageShiftList> {
children: <Widget>[ children: <Widget>[
ShiftHeader( ShiftHeader(
title: shift.roleName, title: shift.roleName,
locationName: shift.locationName,
startTime: _formatTime(shift.timeRange.startsAt), startTime: _formatTime(shift.timeRange.startsAt),
current: shift.assignedWorkerCount, current: shift.assignedWorkerCount,
total: shift.requiredWorkerCount, total: shift.requiredWorkerCount,
@@ -226,9 +227,10 @@ class _CoverageShiftListState extends State<CoverageShiftList> {
worker: worker, worker: worker,
shiftStartTime: _formatTime(shift.timeRange.startsAt), shiftStartTime: _formatTime(shift.timeRange.startsAt),
showRateButton: showRateButton:
worker.status == AssignmentStatus.checkedIn || !worker.hasReview &&
worker.status == AssignmentStatus.checkedOut || (worker.status == AssignmentStatus.checkedIn ||
worker.status == AssignmentStatus.completed, worker.status == AssignmentStatus.checkedOut ||
worker.status == AssignmentStatus.completed),
showCancelButton: showCancelButton:
DateTime.now().isAfter(shift.timeRange.startsAt) && DateTime.now().isAfter(shift.timeRange.startsAt) &&
(worker.status == AssignmentStatus.noShow || (worker.status == AssignmentStatus.noShow ||

View File

@@ -21,6 +21,7 @@ class ShiftHeader extends StatelessWidget {
required this.lateCount, required this.lateCount,
required this.isExpanded, required this.isExpanded,
required this.onToggle, required this.onToggle,
this.locationName,
super.key, super.key,
}); });
@@ -57,6 +58,9 @@ class ShiftHeader extends StatelessWidget {
/// Callback invoked when the header is tapped to expand or collapse. /// Callback invoked when the header is tapped to expand or collapse.
final VoidCallback onToggle; final VoidCallback onToggle;
/// Optional location or hub name for the shift.
final String? locationName;
/// Returns the status colour based on [coveragePercent]. /// Returns the status colour based on [coveragePercent].
/// ///
/// Green for >= 100 %, yellow for >= 80 %, red otherwise. /// Green for >= 100 %, yellow for >= 80 %, red otherwise.
@@ -110,6 +114,29 @@ class ShiftHeader extends StatelessWidget {
title, title,
style: UiTypography.body1b.textPrimary, style: UiTypography.body1b.textPrimary,
), ),
if (locationName != null &&
locationName!.isNotEmpty) ...<Widget>[
const SizedBox(height: 2),
Row(
children: <Widget>[
const Icon(
UiIcons.mapPin,
size: 10,
color: UiColors.textSecondary,
),
const SizedBox(width: 4),
Expanded(
child: Text(
locationName!,
style: UiTypography.body3r.copyWith(
color: UiColors.textSecondary,
),
overflow: TextOverflow.ellipsis,
),
),
],
),
],
const SizedBox(height: UiConstants.space1), const SizedBox(height: UiConstants.space1),
Row( Row(
children: <Widget>[ children: <Widget>[

View File

@@ -228,12 +228,20 @@ class ClockInBloc extends Bloc<ClockInEvent, ClockInState>
event: event, event: event,
activeShiftId: newStatus.activeShiftId, activeShiftId: newStatus.activeShiftId,
); );
} on AppException catch (_) { } on AppException catch (e) {
// The clock-in API call failed. Re-fetch attendance status to // The backend returns 409 ALREADY_CLOCKED_IN when the worker has
// reconcile: if the worker is already clocked in (e.g. duplicate // an active attendance session. This is a normal idempotency
// session from Postgres constraint 23505), treat it as success. // signal — re-fetch the authoritative status and emit success
// without surfacing an error snackbar.
final bool isAlreadyClockedIn =
e is ApiException && e.apiCode == 'ALREADY_CLOCKED_IN';
// Re-fetch attendance status to reconcile local state with
// the backend (handles both ALREADY_CLOCKED_IN and legacy
// Postgres constraint 23505 duplicates).
final AttendanceStatus currentStatus = await _getAttendanceStatus(); final AttendanceStatus currentStatus = await _getAttendanceStatus();
if (currentStatus.isClockedIn) {
if (isAlreadyClockedIn || currentStatus.isClockedIn) {
emit(state.copyWith( emit(state.copyWith(
status: ClockInStatus.success, status: ClockInStatus.success,
attendance: currentStatus, attendance: currentStatus,

View File

@@ -31,6 +31,15 @@ class ProfileRepositoryImpl implements ProfileRepositoryInterface {
return ProfileSectionStatus.fromJson(json); return ProfileSectionStatus.fromJson(json);
} }
@override
Future<StaffReliabilityStats> getReliabilityStats() async {
final ApiResponse response =
await _api.get(StaffEndpoints.profileStats);
final Map<String, dynamic> json =
response.data as Map<String, dynamic>;
return StaffReliabilityStats.fromJson(json);
}
@override @override
Future<void> signOut() async { Future<void> signOut() async {
await _api.post(AuthEndpoints.signOut); await _api.post(AuthEndpoints.signOut);

View File

@@ -3,7 +3,7 @@ import 'package:krow_domain/krow_domain.dart';
/// Abstract interface for the staff profile repository. /// Abstract interface for the staff profile repository.
/// ///
/// Defines the contract for fetching staff profile data, /// Defines the contract for fetching staff profile data,
/// section completion statuses, and signing out. /// section completion statuses, reliability stats, and signing out.
abstract interface class ProfileRepositoryInterface { abstract interface class ProfileRepositoryInterface {
/// Fetches the staff profile from the backend. /// Fetches the staff profile from the backend.
Future<Staff> getStaffProfile(); Future<Staff> getStaffProfile();
@@ -11,6 +11,9 @@ abstract interface class ProfileRepositoryInterface {
/// Fetches the profile section completion statuses. /// Fetches the profile section completion statuses.
Future<ProfileSectionStatus> getProfileSections(); Future<ProfileSectionStatus> getProfileSections();
/// Fetches reliability and performance statistics for the staff member.
Future<StaffReliabilityStats> getReliabilityStats();
/// Signs out the current user. /// Signs out the current user.
Future<void> signOut(); Future<void> signOut();
} }

View File

@@ -0,0 +1,18 @@
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 reliability statistics.
class GetReliabilityStatsUseCase
implements NoInputUseCase<StaffReliabilityStats> {
/// Creates a [GetReliabilityStatsUseCase] with the required [repository].
GetReliabilityStatsUseCase(this._repository);
final ProfileRepositoryInterface _repository;
@override
Future<StaffReliabilityStats> call() {
return _repository.getReliabilityStats();
}
}

View File

@@ -3,6 +3,7 @@ import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart'; import 'package:krow_domain/krow_domain.dart';
import 'package:staff_profile/src/domain/usecases/get_profile_sections_usecase.dart'; import 'package:staff_profile/src/domain/usecases/get_profile_sections_usecase.dart';
import 'package:staff_profile/src/domain/usecases/get_reliability_stats_usecase.dart';
import 'package:staff_profile/src/domain/usecases/get_staff_profile_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/domain/usecases/sign_out_usecase.dart';
import 'package:staff_profile/src/presentation/blocs/profile_state.dart'; import 'package:staff_profile/src/presentation/blocs/profile_state.dart';
@@ -10,21 +11,24 @@ import 'package:staff_profile/src/presentation/blocs/profile_state.dart';
/// Cubit for managing the Profile feature state. /// Cubit for managing the Profile feature state.
/// ///
/// Delegates all data fetching to use cases, following Clean Architecture. /// Delegates all data fetching to use cases, following Clean Architecture.
/// Loads the staff profile and section completion statuses in a single flow. /// Loads the staff profile, section statuses, and reliability stats.
class ProfileCubit extends Cubit<ProfileState> class ProfileCubit extends Cubit<ProfileState>
with BlocErrorHandler<ProfileState> { with BlocErrorHandler<ProfileState> {
/// Creates a [ProfileCubit] with the required use cases. /// Creates a [ProfileCubit] with the required use cases.
ProfileCubit({ ProfileCubit({
required GetStaffProfileUseCase getStaffProfileUseCase, required GetStaffProfileUseCase getStaffProfileUseCase,
required GetProfileSectionsUseCase getProfileSectionsUseCase, required GetProfileSectionsUseCase getProfileSectionsUseCase,
required GetReliabilityStatsUseCase getReliabilityStatsUseCase,
required SignOutUseCase signOutUseCase, required SignOutUseCase signOutUseCase,
}) : _getStaffProfileUseCase = getStaffProfileUseCase, }) : _getStaffProfileUseCase = getStaffProfileUseCase,
_getProfileSectionsUseCase = getProfileSectionsUseCase, _getProfileSectionsUseCase = getProfileSectionsUseCase,
_getReliabilityStatsUseCase = getReliabilityStatsUseCase,
_signOutUseCase = signOutUseCase, _signOutUseCase = signOutUseCase,
super(const ProfileState()); super(const ProfileState());
final GetStaffProfileUseCase _getStaffProfileUseCase; final GetStaffProfileUseCase _getStaffProfileUseCase;
final GetProfileSectionsUseCase _getProfileSectionsUseCase; final GetProfileSectionsUseCase _getProfileSectionsUseCase;
final GetReliabilityStatsUseCase _getReliabilityStatsUseCase;
final SignOutUseCase _signOutUseCase; final SignOutUseCase _signOutUseCase;
/// Loads the staff member's profile. /// Loads the staff member's profile.
@@ -62,6 +66,19 @@ class ProfileCubit extends Cubit<ProfileState>
); );
} }
/// Loads reliability and performance statistics for the staff member.
Future<void> loadReliabilityStats() async {
await handleError(
emit: emit,
action: () async {
final StaffReliabilityStats stats =
await _getReliabilityStatsUseCase();
emit(state.copyWith(reliabilityStats: stats));
},
onError: (String _) => state,
);
}
/// Signs out the current user. /// Signs out the current user.
Future<void> signOut() async { Future<void> signOut() async {
if (state.status == ProfileStatus.loading) { if (state.status == ProfileStatus.loading) {

View File

@@ -28,6 +28,7 @@ class ProfileState extends Equatable {
const ProfileState({ const ProfileState({
this.status = ProfileStatus.initial, this.status = ProfileStatus.initial,
this.profile, this.profile,
this.reliabilityStats,
this.errorMessage, this.errorMessage,
this.personalInfoComplete, this.personalInfoComplete,
this.emergencyContactsComplete, this.emergencyContactsComplete,
@@ -37,40 +38,45 @@ class ProfileState extends Equatable {
this.documentsComplete, this.documentsComplete,
this.certificatesComplete, this.certificatesComplete,
}); });
/// Current status of the profile feature
/// Current status of the profile feature.
final ProfileStatus status; final ProfileStatus status;
/// The staff member's profile object (null if not loaded) /// The staff member's profile object (null if not loaded).
final Staff? profile; final Staff? profile;
/// Error message if status is error /// Reliability and performance statistics (null if not loaded).
final StaffReliabilityStats? reliabilityStats;
/// Error message if status is error.
final String? errorMessage; final String? errorMessage;
/// Whether personal information is complete /// Whether personal information is complete.
final bool? personalInfoComplete; final bool? personalInfoComplete;
/// Whether emergency contacts are complete /// Whether emergency contacts are complete.
final bool? emergencyContactsComplete; final bool? emergencyContactsComplete;
/// Whether experience information is complete /// Whether experience information is complete.
final bool? experienceComplete; final bool? experienceComplete;
/// Whether tax forms are complete /// Whether tax forms are complete.
final bool? taxFormsComplete; final bool? taxFormsComplete;
/// Whether attire options are complete /// Whether attire options are complete.
final bool? attireComplete; final bool? attireComplete;
/// Whether documents are complete /// Whether documents are complete.
final bool? documentsComplete; final bool? documentsComplete;
/// Whether certificates are complete /// Whether certificates are complete.
final bool? certificatesComplete; final bool? certificatesComplete;
/// Creates a copy of this state with updated values. /// Creates a copy of this state with updated values.
ProfileState copyWith({ ProfileState copyWith({
ProfileStatus? status, ProfileStatus? status,
Staff? profile, Staff? profile,
StaffReliabilityStats? reliabilityStats,
String? errorMessage, String? errorMessage,
bool? personalInfoComplete, bool? personalInfoComplete,
bool? emergencyContactsComplete, bool? emergencyContactsComplete,
@@ -83,6 +89,7 @@ class ProfileState extends Equatable {
return ProfileState( return ProfileState(
status: status ?? this.status, status: status ?? this.status,
profile: profile ?? this.profile, profile: profile ?? this.profile,
reliabilityStats: reliabilityStats ?? this.reliabilityStats,
errorMessage: errorMessage ?? this.errorMessage, errorMessage: errorMessage ?? this.errorMessage,
personalInfoComplete: personalInfoComplete ?? this.personalInfoComplete, personalInfoComplete: personalInfoComplete ?? this.personalInfoComplete,
emergencyContactsComplete: emergencyContactsComplete ?? this.emergencyContactsComplete, emergencyContactsComplete: emergencyContactsComplete ?? this.emergencyContactsComplete,
@@ -98,6 +105,7 @@ class ProfileState extends Equatable {
List<Object?> get props => <Object?>[ List<Object?> get props => <Object?>[
status, status,
profile, profile,
reliabilityStats,
errorMessage, errorMessage,
personalInfoComplete, personalInfoComplete,
emergencyContactsComplete, emergencyContactsComplete,

View File

@@ -37,10 +37,11 @@ class StaffProfilePage extends StatelessWidget {
value: cubit, value: cubit,
child: BlocConsumer<ProfileCubit, ProfileState>( child: BlocConsumer<ProfileCubit, ProfileState>(
listener: (BuildContext context, ProfileState state) { listener: (BuildContext context, ProfileState state) {
// Load section statuses when profile loads successfully // Load section statuses and reliability stats when profile loads
if (state.status == ProfileStatus.loaded && if (state.status == ProfileStatus.loaded &&
state.personalInfoComplete == null) { state.personalInfoComplete == null) {
cubit.loadSectionStatuses(); cubit.loadSectionStatuses();
cubit.loadReliabilityStats();
} }
if (state.status == ProfileStatus.signedOut) { if (state.status == ProfileStatus.signedOut) {
@@ -100,16 +101,16 @@ class StaffProfilePage extends StatelessWidget {
children: <Widget>[ children: <Widget>[
// Reliability Stats // Reliability Stats
ReliabilityStatsCard( ReliabilityStatsCard(
totalShifts: 0, totalShifts: state.reliabilityStats?.totalShifts,
averageRating: profile.averageRating, averageRating: state.reliabilityStats?.averageRating,
onTimeRate: 0, onTimeRate: state.reliabilityStats?.onTimeRate.round(),
noShowCount: 0, noShowCount: state.reliabilityStats?.noShowCount,
cancellationCount: 0, cancellationCount: state.reliabilityStats?.cancellationCount,
), ),
// Reliability Score Bar // Reliability Score Bar
const ReliabilityScoreBar( ReliabilityScoreBar(
reliabilityScore: 0, reliabilityScore: state.reliabilityStats?.reliabilityScore.round(),
), ),
// Ordered sections // Ordered sections

View File

@@ -6,6 +6,7 @@ import 'package:krow_domain/krow_domain.dart';
import 'package:staff_profile/src/data/repositories/profile_repository_impl.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/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_profile_sections_usecase.dart';
import 'package:staff_profile/src/domain/usecases/get_reliability_stats_usecase.dart';
import 'package:staff_profile/src/domain/usecases/get_staff_profile_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/domain/usecases/sign_out_usecase.dart';
import 'package:staff_profile/src/presentation/blocs/profile_cubit.dart'; import 'package:staff_profile/src/presentation/blocs/profile_cubit.dart';
@@ -44,12 +45,18 @@ class StaffProfileModule extends Module {
i.get<ProfileRepositoryInterface>(), i.get<ProfileRepositoryInterface>(),
), ),
); );
i.addLazySingleton<GetReliabilityStatsUseCase>(
() => GetReliabilityStatsUseCase(
i.get<ProfileRepositoryInterface>(),
),
);
// Cubit // Cubit
i.addLazySingleton<ProfileCubit>( i.addLazySingleton<ProfileCubit>(
() => ProfileCubit( () => ProfileCubit(
getStaffProfileUseCase: i.get<GetStaffProfileUseCase>(), getStaffProfileUseCase: i.get<GetStaffProfileUseCase>(),
getProfileSectionsUseCase: i.get<GetProfileSectionsUseCase>(), getProfileSectionsUseCase: i.get<GetProfileSectionsUseCase>(),
getReliabilityStatsUseCase: i.get<GetReliabilityStatsUseCase>(),
signOutUseCase: i.get<SignOutUseCase>(), signOutUseCase: i.get<SignOutUseCase>(),
), ),
); );