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