From 789fe24f2b0e27f24d591461c1af3c365ca6a708 Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Mon, 16 Feb 2026 17:38:09 -0500 Subject: [PATCH 1/4] feat: Refactor CoverageModule and CoverageRepositoryImpl to utilize DataConnectService --- .../lib/src/coverage_module.dart | 14 ++--- .../coverage_repository_impl.dart | 59 +++++-------------- 2 files changed, 21 insertions(+), 52 deletions(-) diff --git a/apps/mobile/packages/features/client/client_coverage/lib/src/coverage_module.dart b/apps/mobile/packages/features/client/client_coverage/lib/src/coverage_module.dart index c0cc1258..aa36826c 100644 --- a/apps/mobile/packages/features/client/client_coverage/lib/src/coverage_module.dart +++ b/apps/mobile/packages/features/client/client_coverage/lib/src/coverage_module.dart @@ -10,24 +10,20 @@ import 'presentation/pages/coverage_page.dart'; /// Modular module for the coverage feature. class CoverageModule extends Module { + @override + List get imports => [DataConnectModule()]; + @override void binds(Injector i) { // Repositories - i.addSingleton( - () => CoverageRepositoryImpl(dataConnect: ExampleConnector.instance), - ); + i.addSingleton(CoverageRepositoryImpl.new); // Use Cases i.addSingleton(GetShiftsForDateUseCase.new); i.addSingleton(GetCoverageStatsUseCase.new); // BLoCs - i.addSingleton( - () => CoverageBloc( - getShiftsForDate: i.get(), - getCoverageStats: i.get(), - ), - ); + i.addSingleton(CoverageBloc.new); } @override diff --git a/apps/mobile/packages/features/client/client_coverage/lib/src/data/repositories_impl/coverage_repository_impl.dart b/apps/mobile/packages/features/client/client_coverage/lib/src/data/repositories_impl/coverage_repository_impl.dart index 47a6dbc6..8dec3263 100644 --- a/apps/mobile/packages/features/client/client_coverage/lib/src/data/repositories_impl/coverage_repository_impl.dart +++ b/apps/mobile/packages/features/client/client_coverage/lib/src/data/repositories_impl/coverage_repository_impl.dart @@ -15,44 +15,36 @@ import '../../domain/repositories/coverage_repository.dart'; /// - Returns domain entities from `domain/ui_entities`. class CoverageRepositoryImpl implements CoverageRepository { /// Creates a [CoverageRepositoryImpl]. - CoverageRepositoryImpl({required dc.ExampleConnector dataConnect}) - : _dataConnect = dataConnect; + CoverageRepositoryImpl({required dc.DataConnectService service}) : _service = service; - final dc.ExampleConnector _dataConnect; + final dc.DataConnectService _service; /// Fetches shifts for a specific date. @override Future> getShiftsForDate({required DateTime date}) async { - try { - final String? businessId = - dc.ClientSessionStore.instance.session?.business?.id; - if (businessId == null || businessId.isEmpty) { - return []; - } + return _service.run(() async { + final String businessId = await _service.getBusinessId(); final DateTime start = DateTime(date.year, date.month, date.day); - final DateTime end = - DateTime(date.year, date.month, date.day, 23, 59, 59, 999); + final DateTime end = DateTime(date.year, date.month, date.day, 23, 59, 59, 999); - final fdc.QueryResult< - dc.ListShiftRolesByBusinessAndDateRangeData, - dc.ListShiftRolesByBusinessAndDateRangeVariables> shiftRolesResult = - await _dataConnect + final fdc.QueryResult shiftRolesResult = + await _service.connector .listShiftRolesByBusinessAndDateRange( businessId: businessId, - start: _toTimestamp(start), - end: _toTimestamp(end), + start: _service.toTimestamp(start), + end: _service.toTimestamp(end), ) .execute(); - final fdc.QueryResult< - dc.ListStaffsApplicationsByBusinessForDayData, - dc.ListStaffsApplicationsByBusinessForDayVariables> applicationsResult = - await _dataConnect + final fdc.QueryResult applicationsResult = + await _service.connector .listStaffsApplicationsByBusinessForDay( businessId: businessId, - dayStart: _toTimestamp(start), - dayEnd: _toTimestamp(end), + dayStart: _service.toTimestamp(start), + dayEnd: _service.toTimestamp(end), ) .execute(); @@ -61,18 +53,7 @@ class CoverageRepositoryImpl implements CoverageRepository { applicationsResult.data.applications, date, ); - } catch (e) { - final String error = e.toString().toLowerCase(); - if (error.contains('network') || - error.contains('connection') || - error.contains('unavailable') || - error.contains('offline') || - error.contains('socket') || - error.contains('failed host lookup')) { - throw NetworkException(technicalMessage: 'Coverage fetch failed: $e'); - } - throw ServerException(technicalMessage: 'Coverage fetch failed: $e'); - } + }); } /// Fetches coverage statistics for a specific date. @@ -110,14 +91,6 @@ class CoverageRepositoryImpl implements CoverageRepository { ); } - fdc.Timestamp _toTimestamp(DateTime dateTime) { - final DateTime utc = dateTime.toUtc(); - final int seconds = utc.millisecondsSinceEpoch ~/ 1000; - final int nanoseconds = - (utc.millisecondsSinceEpoch % 1000) * 1000000; - return fdc.Timestamp(nanoseconds, seconds); - } - List _mapCoverageShifts( List shiftRoles, List applications, From a7e8704e4f72717999888a2aa33372f9119039e3 Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Mon, 16 Feb 2026 17:43:20 -0500 Subject: [PATCH 2/4] feat: Simplify HomeRepositoryImpl and ClientHomeModule by using constructor shorthand --- .../features/client/home/lib/client_home.dart | 14 +- .../home_repository_impl.dart | 142 ++++++------------ 2 files changed, 49 insertions(+), 107 deletions(-) diff --git a/apps/mobile/packages/features/client/home/lib/client_home.dart b/apps/mobile/packages/features/client/home/lib/client_home.dart index 65415b46..b72d7b32 100644 --- a/apps/mobile/packages/features/client/home/lib/client_home.dart +++ b/apps/mobile/packages/features/client/home/lib/client_home.dart @@ -23,11 +23,7 @@ class ClientHomeModule extends Module { @override void binds(Injector i) { // Repositories - i.addLazySingleton( - () => HomeRepositoryImpl( - ExampleConnector.instance, - ), - ); + i.addLazySingleton(HomeRepositoryImpl.new); // UseCases i.addLazySingleton(GetDashboardDataUseCase.new); @@ -35,13 +31,7 @@ class ClientHomeModule extends Module { i.addLazySingleton(GetUserSessionDataUseCase.new); // BLoCs - i.add( - () => ClientHomeBloc( - getDashboardDataUseCase: i.get(), - getRecentReordersUseCase: i.get(), - getUserSessionDataUseCase: i.get(), - ), - ); + i.add(ClientHomeBloc.new); } @override diff --git a/apps/mobile/packages/features/client/home/lib/src/data/repositories_impl/home_repository_impl.dart b/apps/mobile/packages/features/client/home/lib/src/data/repositories_impl/home_repository_impl.dart index 547f9c65..cc92dbc8 100644 --- a/apps/mobile/packages/features/client/home/lib/src/data/repositories_impl/home_repository_impl.dart +++ b/apps/mobile/packages/features/client/home/lib/src/data/repositories_impl/home_repository_impl.dart @@ -8,25 +8,14 @@ import '../../domain/repositories/home_repository_interface.dart'; /// This implementation resides in the data layer and acts as a bridge between the /// domain layer and the data source (in this case, a mock from data_connect). class HomeRepositoryImpl implements HomeRepositoryInterface { - /// Creates a [HomeRepositoryImpl]. - HomeRepositoryImpl(this._dataConnect); - final dc.ExampleConnector _dataConnect; + HomeRepositoryImpl(this._service); + final dc.DataConnectService _service; @override Future getDashboardData() async { - try { - final String? businessId = dc.ClientSessionStore.instance.session?.business?.id; - if (businessId == null || businessId.isEmpty) { - return const HomeDashboardData( - weeklySpending: 0, - next7DaysSpending: 0, - weeklyShifts: 0, - next7DaysScheduled: 0, - totalNeeded: 0, - totalFilled: 0, - ); - } + return _service.run(() async { + final String businessId = await _service.getBusinessId(); final DateTime now = DateTime.now(); final int daysFromMonday = now.weekday - DateTime.monday; @@ -35,14 +24,13 @@ class HomeRepositoryImpl implements HomeRepositoryInterface { final DateTime weekRangeStart = DateTime(monday.year, monday.month, monday.day); final DateTime weekRangeEnd = DateTime(monday.year, monday.month, monday.day + 13, 23, 59, 59, 999); - final fdc.QueryResult< - dc.GetCompletedShiftsByBusinessIdData, - dc.GetCompletedShiftsByBusinessIdVariables> completedResult = - await _dataConnect + final fdc.QueryResult completedResult = + await _service.connector .getCompletedShiftsByBusinessId( businessId: businessId, - dateFrom: _toTimestamp(weekRangeStart), - dateTo: _toTimestamp(weekRangeEnd), + dateFrom: _service.toTimestamp(weekRangeStart), + dateTo: _service.toTimestamp(weekRangeEnd), ) .execute(); @@ -50,8 +38,7 @@ class HomeRepositoryImpl implements HomeRepositoryInterface { double next7DaysSpending = 0.0; int weeklyShifts = 0; int next7DaysScheduled = 0; - for (final dc.GetCompletedShiftsByBusinessIdShifts shift - in completedResult.data.shifts) { + for (final dc.GetCompletedShiftsByBusinessIdShifts shift in completedResult.data.shifts) { final DateTime? shiftDate = shift.date?.toDateTime(); if (shiftDate == null) { continue; @@ -73,14 +60,13 @@ class HomeRepositoryImpl implements HomeRepositoryInterface { final DateTime start = DateTime(now.year, now.month, now.day); final DateTime end = DateTime(now.year, now.month, now.day, 23, 59, 59, 999); - final fdc.QueryResult< - dc.ListShiftRolesByBusinessAndDateRangeData, - dc.ListShiftRolesByBusinessAndDateRangeVariables> result = - await _dataConnect + final fdc.QueryResult result = + await _service.connector .listShiftRolesByBusinessAndDateRange( businessId: businessId, - start: _toTimestamp(start), - end: _toTimestamp(end), + start: _service.toTimestamp(start), + end: _service.toTimestamp(end), ) .execute(); @@ -100,18 +86,7 @@ class HomeRepositoryImpl implements HomeRepositoryInterface { totalNeeded: totalNeeded, totalFilled: totalFilled, ); - } catch (e) { - final String error = e.toString().toLowerCase(); - if (error.contains('network') || - error.contains('connection') || - error.contains('unavailable') || - error.contains('offline') || - error.contains('socket') || - error.contains('failed host lookup')) { - throw NetworkException(technicalMessage: 'Home dashboard fetch failed: $e'); - } - throw ServerException(technicalMessage: 'Home dashboard fetch failed: $e'); - } + }); } @override @@ -125,64 +100,41 @@ class HomeRepositoryImpl implements HomeRepositoryInterface { @override Future> getRecentReorders() async { - try { - final String? businessId = dc.ClientSessionStore.instance.session?.business?.id; - if (businessId == null || businessId.isEmpty) { - return const []; - } + return _service.run(() async { + final String businessId = await _service.getBusinessId(); final DateTime now = DateTime.now(); final DateTime start = now.subtract(const Duration(days: 30)); - final fdc.Timestamp startTimestamp = _toTimestamp(start); - final fdc.Timestamp endTimestamp = _toTimestamp(now); + final fdc.Timestamp startTimestamp = _service.toTimestamp(start); + final fdc.Timestamp endTimestamp = _service.toTimestamp(now); - final fdc.QueryResult< - dc.ListShiftRolesByBusinessDateRangeCompletedOrdersData, - dc.ListShiftRolesByBusinessDateRangeCompletedOrdersVariables> result = - await _dataConnect.listShiftRolesByBusinessDateRangeCompletedOrders( - businessId: businessId, - start: startTimestamp, - end: endTimestamp, - ).execute(); + final fdc.QueryResult result = + await _service.connector + .listShiftRolesByBusinessDateRangeCompletedOrders( + businessId: businessId, + start: startTimestamp, + end: endTimestamp, + ) + .execute(); - return result.data.shiftRoles.map(( - dc.ListShiftRolesByBusinessDateRangeCompletedOrdersShiftRoles shiftRole, - ) { - - final String location = - shiftRole.shift.location ?? - shiftRole.shift.locationAddress ?? - ''; - final String type = shiftRole.shift.order.orderType.stringValue; - return ReorderItem( - orderId: shiftRole.shift.order.id, - title: '${shiftRole.role.name} - ${shiftRole.shift.title}', - location: location, - hourlyRate: shiftRole.role.costPerHour, - hours: shiftRole.hours ?? 0, - workers: shiftRole.count, - type: type, - ); - }).toList(); - } catch (e) { - final String error = e.toString().toLowerCase(); - if (error.contains('network') || - error.contains('connection') || - error.contains('unavailable') || - error.contains('offline') || - error.contains('socket') || - error.contains('failed host lookup')) { - throw NetworkException(technicalMessage: 'Home reorders fetch failed: $e'); - } - throw ServerException(technicalMessage: 'Home reorders fetch failed: $e'); - } - } - - fdc.Timestamp _toTimestamp(DateTime date) { - final DateTime utc = date.toUtc(); - final int millis = utc.millisecondsSinceEpoch; - final int seconds = millis ~/ 1000; - final int nanos = (millis % 1000) * 1000000; - return fdc.Timestamp(nanos, seconds); + return result.data.shiftRoles + .map(( + dc.ListShiftRolesByBusinessDateRangeCompletedOrdersShiftRoles shiftRole, + ) { + final String location = shiftRole.shift.location ?? shiftRole.shift.locationAddress ?? ''; + final String type = shiftRole.shift.order.orderType.stringValue; + return ReorderItem( + orderId: shiftRole.shift.order.id, + title: '${shiftRole.role.name} - ${shiftRole.shift.title}', + location: location, + hourlyRate: shiftRole.role.costPerHour, + hours: shiftRole.hours ?? 0, + workers: shiftRole.count, + type: type, + ); + }) + .toList(); + }); } } From 21f0e2ee895a3153cfde25adb5760df8912c6073 Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Mon, 16 Feb 2026 17:43:50 -0500 Subject: [PATCH 3/4] feat: Refactor HubRepositoryImpl to remove FirebaseAuth dependency and utilize DataConnectService --- .../features/client/hubs/lib/client_hubs.dart | 17 +- .../hub_repository_impl.dart | 174 +++++++++--------- 2 files changed, 86 insertions(+), 105 deletions(-) diff --git a/apps/mobile/packages/features/client/hubs/lib/client_hubs.dart b/apps/mobile/packages/features/client/hubs/lib/client_hubs.dart index 088ec6d1..1f7c0eb9 100644 --- a/apps/mobile/packages/features/client/hubs/lib/client_hubs.dart +++ b/apps/mobile/packages/features/client/hubs/lib/client_hubs.dart @@ -3,7 +3,6 @@ library; import 'package:flutter_modular/flutter_modular.dart'; import 'package:krow_core/core.dart'; import 'package:krow_data_connect/krow_data_connect.dart'; -import 'package:firebase_auth/firebase_auth.dart' as firebase; import 'src/data/repositories_impl/hub_repository_impl.dart'; import 'src/domain/repositories/hub_repository_interface.dart'; import 'src/domain/usecases/assign_nfc_tag_usecase.dart'; @@ -23,12 +22,7 @@ class ClientHubsModule extends Module { @override void binds(Injector i) { // Repositories - i.addLazySingleton( - () => HubRepositoryImpl( - firebaseAuth: firebase.FirebaseAuth.instance, - dataConnect: ExampleConnector.instance, - ), - ); + i.addLazySingleton(HubRepositoryImpl.new); // UseCases i.addLazySingleton(GetHubsUseCase.new); @@ -37,14 +31,7 @@ class ClientHubsModule extends Module { i.addLazySingleton(AssignNfcTagUseCase.new); // BLoCs - i.add( - () => ClientHubsBloc( - getHubsUseCase: i.get(), - createHubUseCase: i.get(), - deleteHubUseCase: i.get(), - assignNfcTagUseCase: i.get(), - ), - ); + i.add(ClientHubsBloc.new); } @override diff --git a/apps/mobile/packages/features/client/hubs/lib/src/data/repositories_impl/hub_repository_impl.dart b/apps/mobile/packages/features/client/hubs/lib/src/data/repositories_impl/hub_repository_impl.dart index 2fbd8aac..d207b7d5 100644 --- a/apps/mobile/packages/features/client/hubs/lib/src/data/repositories_impl/hub_repository_impl.dart +++ b/apps/mobile/packages/features/client/hubs/lib/src/data/repositories_impl/hub_repository_impl.dart @@ -9,30 +9,26 @@ import 'package:krow_domain/krow_domain.dart' as domain; import 'package:krow_domain/krow_domain.dart' show HubHasOrdersException, - HubCreationFailedException, BusinessNotFoundException, NotAuthenticatedException; import '../../domain/repositories/hub_repository_interface.dart'; /// Implementation of [HubRepositoryInterface] backed by Data Connect. -class HubRepositoryImpl - with dc.DataErrorHandler - implements HubRepositoryInterface { +class HubRepositoryImpl implements HubRepositoryInterface { HubRepositoryImpl({ - required firebase.FirebaseAuth firebaseAuth, - required dc.ExampleConnector dataConnect, - }) : _firebaseAuth = firebaseAuth, - _dataConnect = dataConnect; + required dc.DataConnectService service, + }) : _service = service; - final firebase.FirebaseAuth _firebaseAuth; - final dc.ExampleConnector _dataConnect; + final dc.DataConnectService _service; @override Future> getHubs() async { - final dc.GetBusinessesByUserIdBusinesses business = await _getBusinessForCurrentUser(); - final String teamId = await _getOrCreateTeamId(business); - return _fetchHubsForTeam(teamId: teamId, businessId: business.id); + return _service.run(() async { + final dc.GetBusinessesByUserIdBusinesses business = await _getBusinessForCurrentUser(); + final String teamId = await _getOrCreateTeamId(business); + return _fetchHubsForTeam(teamId: teamId, businessId: business.id); + }); } @override @@ -48,82 +44,80 @@ class HubRepositoryImpl String? country, String? zipCode, }) async { - final dc.GetBusinessesByUserIdBusinesses business = await _getBusinessForCurrentUser(); - final String teamId = await _getOrCreateTeamId(business); - final _PlaceAddress? placeAddress = - placeId == null || placeId.isEmpty ? null : await _fetchPlaceAddress(placeId); - final String? cityValue = city ?? placeAddress?.city ?? business.city; - final String? stateValue = state ?? placeAddress?.state; - final String? streetValue = street ?? placeAddress?.street; - final String? countryValue = country ?? placeAddress?.country; - final String? zipCodeValue = zipCode ?? placeAddress?.zipCode; + return _service.run(() async { + final dc.GetBusinessesByUserIdBusinesses business = await _getBusinessForCurrentUser(); + final String teamId = await _getOrCreateTeamId(business); + final _PlaceAddress? placeAddress = + placeId == null || placeId.isEmpty ? null : await _fetchPlaceAddress(placeId); + final String? cityValue = city ?? placeAddress?.city ?? business.city; + final String? stateValue = state ?? placeAddress?.state; + final String? streetValue = street ?? placeAddress?.street; + final String? countryValue = country ?? placeAddress?.country; + final String? zipCodeValue = zipCode ?? placeAddress?.zipCode; - final OperationResult - result = await executeProtected(() => _dataConnect - .createTeamHub( - teamId: teamId, - hubName: name, - address: address, - ) - .placeId(placeId) - .latitude(latitude) - .longitude(longitude) - .city(cityValue?.isNotEmpty == true ? cityValue : '') - .state(stateValue) - .street(streetValue) - .country(countryValue) - .zipCode(zipCodeValue) - .execute()); - final String createdId = result.data.teamHub_insert.id; + final OperationResult + result = await _service.connector + .createTeamHub( + teamId: teamId, + hubName: name, + address: address, + ) + .placeId(placeId) + .latitude(latitude) + .longitude(longitude) + .city(cityValue?.isNotEmpty == true ? cityValue : '') + .state(stateValue) + .street(streetValue) + .country(countryValue) + .zipCode(zipCodeValue) + .execute(); + final String createdId = result.data.teamHub_insert.id; - final List hubs = await _fetchHubsForTeam( - teamId: teamId, - businessId: business.id, - ); - domain.Hub? createdHub; - for (final domain.Hub hub in hubs) { - if (hub.id == createdId) { - createdHub = hub; - break; + final List hubs = await _fetchHubsForTeam( + teamId: teamId, + businessId: business.id, + ); + domain.Hub? createdHub; + for (final domain.Hub hub in hubs) { + if (hub.id == createdId) { + createdHub = hub; + break; + } } - } - return createdHub ?? - domain.Hub( - id: createdId, - businessId: business.id, - name: name, - address: address, - nfcTagId: null, - status: domain.HubStatus.active, - ); + return createdHub ?? + domain.Hub( + id: createdId, + businessId: business.id, + name: name, + address: address, + nfcTagId: null, + status: domain.HubStatus.active, + ); + }); } @override Future deleteHub(String id) async { - final String? businessId = dc.ClientSessionStore.instance.session?.business?.id; - if (businessId == null || businessId.isEmpty) { - await _firebaseAuth.signOut(); - throw const BusinessNotFoundException( - technicalMessage: 'Business ID missing from session', - ); - } + return _service.run(() async { + final String businessId = await _service.getBusinessId(); - final QueryResult result = - await executeProtected(() => _dataConnect - .listOrdersByBusinessAndTeamHub( - businessId: businessId, - teamHubId: id, - ) - .execute()); + final QueryResult result = + await _service.connector + .listOrdersByBusinessAndTeamHub( + businessId: businessId, + teamHubId: id, + ) + .execute(); - if (result.data.orders.isNotEmpty) { - throw HubHasOrdersException( - technicalMessage: 'Hub $id has ${result.data.orders.length} orders', - ); - } + if (result.data.orders.isNotEmpty) { + throw HubHasOrdersException( + technicalMessage: 'Hub $id has ${result.data.orders.length} orders', + ); + } - await executeProtected(() => _dataConnect.deleteTeamHub(id: id).execute()); + await _service.connector.deleteTeamHub(id: id).execute(); + }); } @override @@ -141,7 +135,7 @@ class HubRepositoryImpl return dc.GetBusinessesByUserIdBusinesses( id: cachedBusiness.id, businessName: cachedBusiness.businessName, - userId: _firebaseAuth.currentUser?.uid ?? '', + userId: _service.auth.currentUser?.uid ?? '', rateGroup: const dc.Known(dc.BusinessRateGroup.STANDARD), status: const dc.Known(dc.BusinessStatus.ACTIVE), contactName: cachedBusiness.contactName, @@ -159,7 +153,7 @@ class HubRepositoryImpl ); } - final firebase.User? user = _firebaseAuth.currentUser; + final firebase.User? user = _service.auth.currentUser; if (user == null) { throw const NotAuthenticatedException( technicalMessage: 'No Firebase user in currentUser', @@ -168,11 +162,11 @@ class HubRepositoryImpl final QueryResult result = - await executeProtected(() => _dataConnect.getBusinessesByUserId( + await _service.connector.getBusinessesByUserId( userId: user.uid, - ).execute()); + ).execute(); if (result.data.businesses.isEmpty) { - await _firebaseAuth.signOut(); + await _service.auth.signOut(); throw BusinessNotFoundException( technicalMessage: 'No business found for user ${user.uid}', ); @@ -203,14 +197,14 @@ class HubRepositoryImpl dc.GetBusinessesByUserIdBusinesses business, ) async { final QueryResult - teamsResult = await executeProtected(() => _dataConnect.getTeamsByOwnerId( + teamsResult = await _service.connector.getTeamsByOwnerId( ownerId: business.id, - ).execute()); + ).execute(); if (teamsResult.data.teams.isNotEmpty) { return teamsResult.data.teams.first.id; } - final dc.CreateTeamVariablesBuilder createTeamBuilder = _dataConnect.createTeam( + final dc.CreateTeamVariablesBuilder createTeamBuilder = _service.connector.createTeam( teamName: '${business.businessName} Team', ownerId: business.id, ownerName: business.contactName ?? '', @@ -222,7 +216,7 @@ class HubRepositoryImpl final OperationResult createTeamResult = - await executeProtected(() => createTeamBuilder.execute()); + await createTeamBuilder.execute(); final String teamId = createTeamResult.data.team_insert.id; return teamId; @@ -234,9 +228,9 @@ class HubRepositoryImpl }) async { final QueryResult hubsResult = - await executeProtected(() => _dataConnect.getTeamHubsByTeamId( + await _service.connector.getTeamHubsByTeamId( teamId: teamId, - ).execute()); + ).execute(); return hubsResult.data.teamHubs .map( From 39bb17d981bfb2b2aa8d9096c7295e126f982262 Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Mon, 16 Feb 2026 17:49:34 -0500 Subject: [PATCH 4/4] feat: Refactor repositories and modules to remove FirebaseAuth dependency and utilize DataConnectService --- .../pages/client_sign_up_page.dart | 2 +- .../src/presentation/blocs/billing_bloc.dart | 1 - .../client_create_order_repository_impl.dart | 3 +- .../widgets/coverage_dashboard.dart | 8 +- .../client/settings/lib/client_settings.dart | 13 +- .../settings_repository_impl.dart | 20 +- .../view_orders_repository_impl.dart | 214 ++++++++---------- .../presentation/widgets/view_order_card.dart | 22 +- .../lib/src/view_orders_module.dart | 15 +- 9 files changed, 121 insertions(+), 177 deletions(-) diff --git a/apps/mobile/packages/features/client/authentication/lib/src/presentation/pages/client_sign_up_page.dart b/apps/mobile/packages/features/client/authentication/lib/src/presentation/pages/client_sign_up_page.dart index c6b8425f..99975735 100644 --- a/apps/mobile/packages/features/client/authentication/lib/src/presentation/pages/client_sign_up_page.dart +++ b/apps/mobile/packages/features/client/authentication/lib/src/presentation/pages/client_sign_up_page.dart @@ -41,7 +41,7 @@ class ClientSignUpPage extends StatelessWidget { final TranslationsClientAuthenticationSignUpPageEn i18n = t.client_authentication.sign_up_page; final ClientAuthBloc authBloc = Modular.get(); - return BlocProvider.value( + return BlocProvider.value( value: authBloc, child: BlocConsumer( listener: (BuildContext context, ClientAuthState state) { diff --git a/apps/mobile/packages/features/client/billing/lib/src/presentation/blocs/billing_bloc.dart b/apps/mobile/packages/features/client/billing/lib/src/presentation/blocs/billing_bloc.dart index 23b18b1f..ccddda07 100644 --- a/apps/mobile/packages/features/client/billing/lib/src/presentation/blocs/billing_bloc.dart +++ b/apps/mobile/packages/features/client/billing/lib/src/presentation/blocs/billing_bloc.dart @@ -54,7 +54,6 @@ class BillingBloc extends Bloc _getSpendingBreakdown.call(state.period), ]); - final double currentBill = results[0] as double; final double savings = results[1] as double; final List pendingInvoices = results[2] as List; final List invoiceHistory = results[3] as List; diff --git a/apps/mobile/packages/features/client/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart b/apps/mobile/packages/features/client/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart index 0ae65a1a..d5c90dea 100644 --- a/apps/mobile/packages/features/client/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart +++ b/apps/mobile/packages/features/client/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart @@ -6,8 +6,7 @@ import '../../domain/repositories/client_create_order_repository_interface.dart' /// Implementation of [ClientCreateOrderRepositoryInterface]. /// -/// This implementation coordinates data access for order creation by delegating -/// to the [OrderRepositoryMock] and [ExampleConnector] from the shared +/// This implementation coordinates data access for order creation by [DataConnectService] from the shared /// Data Connect package. /// /// It follows the KROW Clean Architecture by keeping the data layer focused diff --git a/apps/mobile/packages/features/client/home/lib/src/presentation/widgets/coverage_dashboard.dart b/apps/mobile/packages/features/client/home/lib/src/presentation/widgets/coverage_dashboard.dart index ee6bf227..a85eca69 100644 --- a/apps/mobile/packages/features/client/home/lib/src/presentation/widgets/coverage_dashboard.dart +++ b/apps/mobile/packages/features/client/home/lib/src/presentation/widgets/coverage_dashboard.dart @@ -22,8 +22,8 @@ class CoverageDashboard extends StatelessWidget { int totalConfirmed = 0; double todayCost = 0; - for (final s in shifts) { - final int needed = s['workersNeeded'] as int? ?? 0; + for (final dynamic s in shifts) { + final int needed = (s as Map)['workersNeeded'] as int? ?? 0; final int confirmed = s['filled'] as int? ?? 0; final double rate = s['hourlyRate'] as double? ?? 0.0; final double hours = s['hours'] as double? ?? 0.0; @@ -39,10 +39,10 @@ class CoverageDashboard extends StatelessWidget { final int unfilledPositions = totalNeeded - totalConfirmed; final int checkedInCount = applications - .where((a) => (a as Map)['checkInTime'] != null) + .where((dynamic a) => (a as Map)['checkInTime'] != null) .length; final int lateWorkersCount = applications - .where((a) => (a as Map)['status'] == 'LATE') + .where((dynamic a) => (a as Map)['status'] == 'LATE') .length; final bool isCoverageGood = coveragePercent >= 90; diff --git a/apps/mobile/packages/features/client/settings/lib/client_settings.dart b/apps/mobile/packages/features/client/settings/lib/client_settings.dart index 1af20a06..90cb283e 100644 --- a/apps/mobile/packages/features/client/settings/lib/client_settings.dart +++ b/apps/mobile/packages/features/client/settings/lib/client_settings.dart @@ -1,6 +1,6 @@ -import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter_modular/flutter_modular.dart'; import 'package:krow_core/core.dart'; +import 'package:krow_data_connect/krow_data_connect.dart'; import 'src/data/repositories_impl/settings_repository_impl.dart'; import 'src/domain/repositories/settings_repository_interface.dart'; import 'src/domain/usecases/sign_out_usecase.dart'; @@ -9,20 +9,19 @@ import 'src/presentation/pages/client_settings_page.dart'; /// A [Module] for the client settings feature. class ClientSettingsModule extends Module { + @override + List get imports => [DataConnectModule()]; + @override void binds(Injector i) { // Repositories - i.addLazySingleton( - () => SettingsRepositoryImpl(firebaseAuth: FirebaseAuth.instance), - ); + i.addLazySingleton(SettingsRepositoryImpl.new); // UseCases i.addLazySingleton(SignOutUseCase.new); // BLoCs - i.add( - () => ClientSettingsBloc(signOutUseCase: i.get()), - ); + i.add(ClientSettingsBloc.new); } @override diff --git a/apps/mobile/packages/features/client/settings/lib/src/data/repositories_impl/settings_repository_impl.dart b/apps/mobile/packages/features/client/settings/lib/src/data/repositories_impl/settings_repository_impl.dart index 948af68c..2da4bc85 100644 --- a/apps/mobile/packages/features/client/settings/lib/src/data/repositories_impl/settings_repository_impl.dart +++ b/apps/mobile/packages/features/client/settings/lib/src/data/repositories_impl/settings_repository_impl.dart @@ -1,23 +1,21 @@ -import 'package:firebase_auth/firebase_auth.dart'; +import 'package:krow_data_connect/krow_data_connect.dart' as dc; import '../../domain/repositories/settings_repository_interface.dart'; /// Implementation of [SettingsRepositoryInterface]. /// -/// This implementation delegates authentication operations to [FirebaseAuth]. +/// This implementation delegates authentication operations to [DataConnectService]. class SettingsRepositoryImpl implements SettingsRepositoryInterface { -/// Creates a [SettingsRepositoryImpl] with the required [_firebaseAuth]. - const SettingsRepositoryImpl({required this.firebaseAuth}); + /// Creates a [SettingsRepositoryImpl] with the required [_service]. + const SettingsRepositoryImpl({required dc.DataConnectService service}) : _service = service; - /// The Firebase Auth instance. - final FirebaseAuth firebaseAuth; + /// The Data Connect service. + final dc.DataConnectService _service; @override Future signOut() async { - try { - await firebaseAuth.signOut(); - } catch (e) { - throw Exception('Error signing out: ${e.toString()}'); - } + return _service.run(() async { + await _service.auth.signOut(); + }); } } diff --git a/apps/mobile/packages/features/client/view_orders/lib/src/data/repositories/view_orders_repository_impl.dart b/apps/mobile/packages/features/client/view_orders/lib/src/data/repositories/view_orders_repository_impl.dart index 82e730fc..2886c335 100644 --- a/apps/mobile/packages/features/client/view_orders/lib/src/data/repositories/view_orders_repository_impl.dart +++ b/apps/mobile/packages/features/client/view_orders/lib/src/data/repositories/view_orders_repository_impl.dart @@ -1,4 +1,3 @@ -import 'package:firebase_auth/firebase_auth.dart' as firebase; import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc; import 'package:intl/intl.dart'; import 'package:krow_data_connect/krow_data_connect.dart' as dc; @@ -6,151 +5,132 @@ import 'package:krow_domain/krow_domain.dart' as domain; import '../../domain/repositories/i_view_orders_repository.dart'; /// Implementation of [IViewOrdersRepository] using Data Connect. -class ViewOrdersRepositoryImpl - with dc.DataErrorHandler - implements IViewOrdersRepository { - final firebase.FirebaseAuth _firebaseAuth; - final dc.ExampleConnector _dataConnect; +class ViewOrdersRepositoryImpl implements IViewOrdersRepository { + final dc.DataConnectService _service; ViewOrdersRepositoryImpl({ - required firebase.FirebaseAuth firebaseAuth, - required dc.ExampleConnector dataConnect, - }) : _firebaseAuth = firebaseAuth, - _dataConnect = dataConnect; + required dc.DataConnectService service, + }) : _service = service; @override Future> getOrdersForRange({ required DateTime start, required DateTime end, }) async { - final String? businessId = dc.ClientSessionStore.instance.session?.business?.id; - if (businessId == null || businessId.isEmpty) { - await _firebaseAuth.signOut(); - throw Exception('Business is missing. Please sign in again.'); - } - - final fdc.Timestamp startTimestamp = _toTimestamp(_startOfDay(start)); - final fdc.Timestamp endTimestamp = _toTimestamp(_endOfDay(end)); - final fdc.QueryResult result = - await executeProtected(() => _dataConnect - .listShiftRolesByBusinessAndDateRange( - businessId: businessId, - start: startTimestamp, - end: endTimestamp, - ) - .execute()); - print( - 'ViewOrders range start=${start.toIso8601String()} end=${end.toIso8601String()} shiftRoles=${result.data.shiftRoles.length}', - ); - - final String businessName = - dc.ClientSessionStore.instance.session?.business?.businessName ?? - 'Your Company'; - - return result.data.shiftRoles.map((dc.ListShiftRolesByBusinessAndDateRangeShiftRoles shiftRole) { - final DateTime? shiftDate = shiftRole.shift.date?.toDateTime().toLocal(); - final String dateStr = shiftDate == null - ? '' - : DateFormat('yyyy-MM-dd').format(shiftDate); - final String startTime = _formatTime(shiftRole.startTime); - final String endTime = _formatTime(shiftRole.endTime); - final int filled = shiftRole.assigned ?? 0; - final int workersNeeded = shiftRole.count; - final double hours = shiftRole.hours ?? 0; - final double totalValue = shiftRole.totalValue ?? 0; - final double hourlyRate = _hourlyRate(shiftRole.totalValue, shiftRole.hours); - // final String status = filled >= workersNeeded ? 'filled' : 'open'; - final String status = shiftRole.shift.status?.stringValue ?? 'OPEN'; + return _service.run(() async { + final String businessId = await _service.getBusinessId(); + final fdc.Timestamp startTimestamp = _service.toTimestamp(_startOfDay(start)); + final fdc.Timestamp endTimestamp = _service.toTimestamp(_endOfDay(end)); + final fdc.QueryResult result = + await _service.connector + .listShiftRolesByBusinessAndDateRange( + businessId: businessId, + start: startTimestamp, + end: endTimestamp, + ) + .execute(); print( - 'ViewOrders item: date=$dateStr status=$status shiftId=${shiftRole.shiftId} ' - 'roleId=${shiftRole.roleId} start=${shiftRole.startTime?.toJson()} ' - 'end=${shiftRole.endTime?.toJson()} hours=$hours totalValue=$totalValue', + 'ViewOrders range start=${start.toIso8601String()} end=${end.toIso8601String()} shiftRoles=${result.data.shiftRoles.length}', ); - final String eventName = - shiftRole.shift.order.eventName ?? shiftRole.shift.title; + final String businessName = + dc.ClientSessionStore.instance.session?.business?.businessName ?? 'Your Company'; - return domain.OrderItem( - id: _shiftRoleKey(shiftRole.shiftId, shiftRole.roleId), - orderId: shiftRole.shift.order.id, - title: '${shiftRole.role.name} - $eventName', - clientName: businessName, - status: status, - date: dateStr, - startTime: startTime, - endTime: endTime, - location: shiftRole.shift.location ?? '', - locationAddress: shiftRole.shift.locationAddress ?? '', - filled: filled, - workersNeeded: workersNeeded, - hourlyRate: hourlyRate, - hours: hours, - totalValue: totalValue, - confirmedApps: const >[], - ); - }).toList(); + return result.data.shiftRoles.map((dc.ListShiftRolesByBusinessAndDateRangeShiftRoles shiftRole) { + final DateTime? shiftDate = shiftRole.shift.date?.toDateTime().toLocal(); + final String dateStr = shiftDate == null ? '' : DateFormat('yyyy-MM-dd').format(shiftDate); + final String startTime = _formatTime(shiftRole.startTime); + final String endTime = _formatTime(shiftRole.endTime); + final int filled = shiftRole.assigned ?? 0; + final int workersNeeded = shiftRole.count; + final double hours = shiftRole.hours ?? 0; + final double totalValue = shiftRole.totalValue ?? 0; + final double hourlyRate = _hourlyRate(shiftRole.totalValue, shiftRole.hours); + // final String status = filled >= workersNeeded ? 'filled' : 'open'; + final String status = shiftRole.shift.status?.stringValue ?? 'OPEN'; + + print( + 'ViewOrders item: date=$dateStr status=$status shiftId=${shiftRole.shiftId} ' + 'roleId=${shiftRole.roleId} start=${shiftRole.startTime?.toJson()} ' + 'end=${shiftRole.endTime?.toJson()} hours=$hours totalValue=$totalValue', + ); + + final String eventName = shiftRole.shift.order.eventName ?? shiftRole.shift.title; + + return domain.OrderItem( + id: _shiftRoleKey(shiftRole.shiftId, shiftRole.roleId), + orderId: shiftRole.shift.order.id, + title: '${shiftRole.role.name} - $eventName', + clientName: businessName, + status: status, + date: dateStr, + startTime: startTime, + endTime: endTime, + location: shiftRole.shift.location ?? '', + locationAddress: shiftRole.shift.locationAddress ?? '', + filled: filled, + workersNeeded: workersNeeded, + hourlyRate: hourlyRate, + hours: hours, + totalValue: totalValue, + confirmedApps: const >[], + ); + }).toList(); + }); } @override Future>>> getAcceptedApplicationsForDay( DateTime day, ) async { - final String? businessId = dc.ClientSessionStore.instance.session?.business?.id; - if (businessId == null || businessId.isEmpty) { - await _firebaseAuth.signOut(); - throw Exception('Business is missing. Please sign in again.'); - } + return _service.run(() async { + final String businessId = await _service.getBusinessId(); - final fdc.Timestamp dayStart = _toTimestamp(_startOfDay(day)); - final fdc.Timestamp dayEnd = _toTimestamp(_endOfDay(day)); - final fdc.QueryResult result = - await executeProtected(() => _dataConnect - .listAcceptedApplicationsByBusinessForDay( - businessId: businessId, - dayStart: dayStart, - dayEnd: dayEnd, - ) - .execute()); + final fdc.Timestamp dayStart = _service.toTimestamp(_startOfDay(day)); + final fdc.Timestamp dayEnd = _service.toTimestamp(_endOfDay(day)); + final fdc.QueryResult result = + await _service.connector + .listAcceptedApplicationsByBusinessForDay( + businessId: businessId, + dayStart: dayStart, + dayEnd: dayEnd, + ) + .execute(); - print( - 'ViewOrders day=${day.toIso8601String()} applications=${result.data.applications.length}', - ); - - final Map>> grouped = >>{}; - for (final dc.ListAcceptedApplicationsByBusinessForDayApplications application in result.data.applications) { print( - 'ViewOrders app: shiftId=${application.shiftId} roleId=${application.roleId} ' - 'checkIn=${application.checkInTime?.toJson()} checkOut=${application.checkOutTime?.toJson()}', + 'ViewOrders day=${day.toIso8601String()} applications=${result.data.applications.length}', ); - final String key = _shiftRoleKey(application.shiftId, application.roleId); - grouped.putIfAbsent(key, () => >[]); - grouped[key]!.add({ - 'id': application.id, - 'worker_id': application.staff.id, - 'worker_name': application.staff.fullName, - 'status': 'confirmed', - 'photo_url': application.staff.photoUrl, - 'phone': application.staff.phone, - 'rating': application.staff.averageRating, - }); - } - return grouped; + + final Map>> grouped = >>{}; + for (final dc.ListAcceptedApplicationsByBusinessForDayApplications application + in result.data.applications) { + print( + 'ViewOrders app: shiftId=${application.shiftId} roleId=${application.roleId} ' + 'checkIn=${application.checkInTime?.toJson()} checkOut=${application.checkOutTime?.toJson()}', + ); + final String key = _shiftRoleKey(application.shiftId, application.roleId); + grouped.putIfAbsent(key, () => >[]); + grouped[key]!.add({ + 'id': application.id, + 'worker_id': application.staff.id, + 'worker_name': application.staff.fullName, + 'status': 'confirmed', + 'photo_url': application.staff.photoUrl, + 'phone': application.staff.phone, + 'rating': application.staff.averageRating, + }); + } + return grouped; + }); } String _shiftRoleKey(String shiftId, String roleId) { return '$shiftId:$roleId'; } - fdc.Timestamp _toTimestamp(DateTime dateTime) { - final DateTime utc = dateTime.toUtc(); - final int seconds = utc.millisecondsSinceEpoch ~/ 1000; - final int nanoseconds = (utc.microsecondsSinceEpoch % 1000000) * 1000; - return fdc.Timestamp(nanoseconds, seconds); - } - DateTime _startOfDay(DateTime dateTime) { return DateTime(dateTime.year, dateTime.month, dateTime.day); } diff --git a/apps/mobile/packages/features/client/view_orders/lib/src/presentation/widgets/view_order_card.dart b/apps/mobile/packages/features/client/view_orders/lib/src/presentation/widgets/view_order_card.dart index 480faece..0f875aff 100644 --- a/apps/mobile/packages/features/client/view_orders/lib/src/presentation/widgets/view_order_card.dart +++ b/apps/mobile/packages/features/client/view_orders/lib/src/presentation/widgets/view_order_card.dart @@ -80,23 +80,6 @@ class _ViewOrderCardState extends State { } } - /// Formats the date string for display. - String _formatDate({required String dateStr}) { - try { - final DateTime date = DateTime.parse(dateStr); - final DateTime now = DateTime.now(); - final DateTime today = DateTime(now.year, now.month, now.day); - final DateTime tomorrow = today.add(const Duration(days: 1)); - final DateTime checkDate = DateTime(date.year, date.month, date.day); - - if (checkDate == today) return t.client_view_orders.card.today; - if (checkDate == tomorrow) return t.client_view_orders.card.tomorrow; - return DateFormat('EEE, MMM d').format(date); - } catch (_) { - return dateStr; - } - } - /// Formats the time string for display. String _formatTime({required String timeStr}) { if (timeStr.isEmpty) return ''; @@ -829,10 +812,7 @@ class _OrderEditSheetState extends State<_OrderEditSheet> { final String dateText = orderDate == null ? widget.order.date : DateFormat('yyyy-MM-dd').format(orderDate); - final String location = firstShift.order.teamHub?.hubName ?? - firstShift.locationAddress ?? - firstShift.location ?? - widget.order.locationAddress; + final String location = firstShift.order.teamHub.hubName; _dateController.text = dateText; _globalLocationController.text = location; diff --git a/apps/mobile/packages/features/client/view_orders/lib/src/view_orders_module.dart b/apps/mobile/packages/features/client/view_orders/lib/src/view_orders_module.dart index ceac0b36..b23db650 100644 --- a/apps/mobile/packages/features/client/view_orders/lib/src/view_orders_module.dart +++ b/apps/mobile/packages/features/client/view_orders/lib/src/view_orders_module.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_modular/flutter_modular.dart'; import 'package:krow_data_connect/krow_data_connect.dart'; -import 'package:firebase_auth/firebase_auth.dart' as firebase; import 'data/repositories/view_orders_repository_impl.dart'; import 'domain/repositories/i_view_orders_repository.dart'; @@ -21,24 +20,14 @@ class ViewOrdersModule extends Module { @override void binds(Injector i) { // Repositories - i.add( - () => ViewOrdersRepositoryImpl( - firebaseAuth: firebase.FirebaseAuth.instance, - dataConnect: ExampleConnector.instance, - ), - ); + i.add(ViewOrdersRepositoryImpl.new); // UseCases i.add(GetOrdersUseCase.new); i.add(GetAcceptedApplicationsForDayUseCase.new); // BLoCs - i.add( - () => ViewOrdersCubit( - getOrdersUseCase: i.get(), - getAcceptedAppsUseCase: i.get(), - ), - ); + i.add(ViewOrdersCubit.new); } @override