From 1ba83e3ea6e228114d5f1c95768a75c020b1fbea Mon Sep 17 00:00:00 2001 From: Suriya Date: Wed, 4 Feb 2026 12:30:20 +0530 Subject: [PATCH] feat: Implement Google Places Autocomplete for Staff Location - Implemented strictly filtered Google Places Autocomplete (cities only) for Staff Profile Setup. - Centralized Google Places API Key configuration in Core AppConfig. - Updated Client Hubs to use the centralized AppConfig. - Verified ViewOrdersCubit logic for weekly order summaries. --- apps/mobile/packages/core/lib/core.dart | 1 + .../core/lib/src/config/app_config.dart | 5 + .../hubs/lib/src/util/hubs_constants.dart | 4 +- .../features/client/view_orders/error.txt | 0 .../presentation/blocs/view_orders_cubit.dart | 21 +--- .../blocs/view_orders_cubit_test.dart | 82 ++++++++++++ .../place_repository_impl.dart | 49 ++++++++ .../domain/repositories/place_repository.dart | 5 + .../usecases/search_cities_usecase.dart | 11 ++ .../profile_setup/profile_setup_bloc.dart | 32 +++++ .../profile_setup/profile_setup_event.dart | 18 +++ .../profile_setup/profile_setup_state.dart | 26 ++-- .../profile_setup_location.dart | 118 +++++++++++------- .../lib/staff_authentication.dart | 6 + .../staff/authentication/pubspec.yaml | 1 + 15 files changed, 303 insertions(+), 76 deletions(-) create mode 100644 apps/mobile/packages/core/lib/src/config/app_config.dart create mode 100644 apps/mobile/packages/features/client/view_orders/error.txt create mode 100644 apps/mobile/packages/features/client/view_orders/test/src/presentation/blocs/view_orders_cubit_test.dart create mode 100644 apps/mobile/packages/features/staff/authentication/lib/src/data/repositories_impl/place_repository_impl.dart create mode 100644 apps/mobile/packages/features/staff/authentication/lib/src/domain/repositories/place_repository.dart create mode 100644 apps/mobile/packages/features/staff/authentication/lib/src/domain/usecases/search_cities_usecase.dart diff --git a/apps/mobile/packages/core/lib/core.dart b/apps/mobile/packages/core/lib/core.dart index 0b9e5ccf..819c596a 100644 --- a/apps/mobile/packages/core/lib/core.dart +++ b/apps/mobile/packages/core/lib/core.dart @@ -4,3 +4,4 @@ export 'src/domain/arguments/usecase_argument.dart'; export 'src/domain/usecases/usecase.dart'; export 'src/utils/date_time_utils.dart'; export 'src/presentation/widgets/web_mobile_frame.dart'; +export 'src/config/app_config.dart'; diff --git a/apps/mobile/packages/core/lib/src/config/app_config.dart b/apps/mobile/packages/core/lib/src/config/app_config.dart new file mode 100644 index 00000000..5e652bd0 --- /dev/null +++ b/apps/mobile/packages/core/lib/src/config/app_config.dart @@ -0,0 +1,5 @@ +class AppConfig { + AppConfig._(); + + static const String googlePlacesApiKey = String.fromEnvironment('GOOGLE_PLACES_API_KEY'); +} diff --git a/apps/mobile/packages/features/client/hubs/lib/src/util/hubs_constants.dart b/apps/mobile/packages/features/client/hubs/lib/src/util/hubs_constants.dart index 23d706bc..13eae839 100644 --- a/apps/mobile/packages/features/client/hubs/lib/src/util/hubs_constants.dart +++ b/apps/mobile/packages/features/client/hubs/lib/src/util/hubs_constants.dart @@ -1,4 +1,6 @@ +import 'package:krow_core/krow_core.dart'; + class HubsConstants { - static const String googlePlacesApiKey = String.fromEnvironment('GOOGLE_PLACES_API_KEY'); + static const String googlePlacesApiKey = AppConfig.googlePlacesApiKey; static const List supportedCountries = ['us']; } diff --git a/apps/mobile/packages/features/client/view_orders/error.txt b/apps/mobile/packages/features/client/view_orders/error.txt new file mode 100644 index 00000000..e69de29b diff --git a/apps/mobile/packages/features/client/view_orders/lib/src/presentation/blocs/view_orders_cubit.dart b/apps/mobile/packages/features/client/view_orders/lib/src/presentation/blocs/view_orders_cubit.dart index f65f4964..55d5c06c 100644 --- a/apps/mobile/packages/features/client/view_orders/lib/src/presentation/blocs/view_orders_cubit.dart +++ b/apps/mobile/packages/features/client/view_orders/lib/src/presentation/blocs/view_orders_cubit.dart @@ -255,20 +255,12 @@ class ViewOrdersCubit extends Cubit { } int _calculateCategoryCount(String category) { - if (state.selectedDate == null) return 0; - final String selectedDateStr = DateFormat( - 'yyyy-MM-dd', - ).format(state.selectedDate!); - final List ordersOnDate = state.orders - .where((OrderItem s) => s.date == selectedDateStr) - .toList(); - if (category == 'active') { - return ordersOnDate + return state.orders .where((OrderItem s) => s.status == 'IN_PROGRESS') .length; } else if (category == 'completed') { - return ordersOnDate + return state.orders .where((OrderItem s) => s.status == 'COMPLETED') .length; } @@ -276,14 +268,7 @@ class ViewOrdersCubit extends Cubit { } int _calculateUpNextCount() { - if (state.selectedDate == null) return 0; - final String selectedDateStr = DateFormat( - 'yyyy-MM-dd', - ).format(state.selectedDate!); - final List ordersOnDate = state.orders - .where((OrderItem s) => s.date == selectedDateStr) - .toList(); - return ordersOnDate + return state.orders .where( (OrderItem s) => // TODO(orders): move PENDING to its own tab once available. diff --git a/apps/mobile/packages/features/client/view_orders/test/src/presentation/blocs/view_orders_cubit_test.dart b/apps/mobile/packages/features/client/view_orders/test/src/presentation/blocs/view_orders_cubit_test.dart new file mode 100644 index 00000000..27e68494 --- /dev/null +++ b/apps/mobile/packages/features/client/view_orders/test/src/presentation/blocs/view_orders_cubit_test.dart @@ -0,0 +1,82 @@ +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:view_orders/src/presentation/blocs/view_orders_cubit.dart'; +import 'package:view_orders/src/presentation/blocs/view_orders_state.dart'; +import 'package:view_orders/src/domain/usecases/get_orders_use_case.dart'; +import 'package:view_orders/src/domain/usecases/get_accepted_applications_for_day_use_case.dart'; +import 'package:krow_domain/krow_domain.dart'; +import 'package:view_orders/src/domain/arguments/orders_range_arguments.dart'; +import 'package:view_orders/src/domain/arguments/orders_day_arguments.dart'; + +class MockGetOrdersUseCase extends Mock implements GetOrdersUseCase {} +class MockGetAcceptedAppsUseCase extends Mock implements GetAcceptedApplicationsForDayUseCase {} + +void main() { + group('ViewOrdersCubit', () { + late GetOrdersUseCase getOrdersUseCase; + late GetAcceptedApplicationsForDayUseCase getAcceptedAppsUseCase; + + setUp(() { + getOrdersUseCase = MockGetOrdersUseCase(); + getAcceptedAppsUseCase = MockGetAcceptedAppsUseCase(); + registerFallbackValue(OrdersRangeArguments(start: DateTime.now(), end: DateTime.now())); + registerFallbackValue(OrdersDayArguments(day: DateTime.now())); + }); + + test('initial state is correct', () { + final cubit = ViewOrdersCubit( + getOrdersUseCase: getOrdersUseCase, + getAcceptedAppsUseCase: getAcceptedAppsUseCase, + ); + expect(cubit.state.status, ViewOrdersStatus.initial); + cubit.close(); + }); + + blocTest( + 'calculates upNextCount based on ALL loaded orders, not just the selected day', + build: () { + final mockOrders = [ + // Order 1: Today (Matches selected date) + OrderItem( + id: '1', orderId: '1', title: 'Order 1', clientName: 'Client', + status: 'OPEN', date: '2026-02-04', startTime: '09:00', endTime: '17:00', + location: 'Loc', locationAddress: 'Addr', filled: 0, workersNeeded: 1, + hourlyRate: 20, hours: 8, totalValue: 160 + ), + // Order 2: Tomorrow (Different date) + OrderItem( + id: '2', orderId: '2', title: 'Order 2', clientName: 'Client', + status: 'OPEN', date: '2026-02-05', startTime: '09:00', endTime: '17:00', + location: 'Loc', locationAddress: 'Addr', filled: 0, workersNeeded: 1, + hourlyRate: 20, hours: 8, totalValue: 160 + ), + ]; + + when(() => getOrdersUseCase(any())).thenAnswer((_) async => mockOrders); + when(() => getAcceptedAppsUseCase(any())).thenAnswer((_) async => {}); + + return ViewOrdersCubit( + getOrdersUseCase: getOrdersUseCase, + getAcceptedAppsUseCase: getAcceptedAppsUseCase, + ); + }, + act: (cubit) async { + // Wait for init to trigger load + await Future.delayed(const Duration(milliseconds: 100)); + + // Select 'Today' (2026-02-04 matches Order 1) + cubit.selectDate(DateTime(2026, 02, 04)); + }, + verify: (cubit) { + // Assert: + // 1. filteredOrders should only have 1 order (the one for the selected date) + expect(cubit.state.filteredOrders.length, 1, reason: 'Should only show orders for selected filtered date'); + expect(cubit.state.filteredOrders.first.id, '1'); + + // 2. upNextCount should have 2 orders (Total for the loaded week) + expect(cubit.state.upNextCount, 2, reason: 'Up Next count should include ALL orders in the week range'); + }, + ); + }); +} diff --git a/apps/mobile/packages/features/staff/authentication/lib/src/data/repositories_impl/place_repository_impl.dart b/apps/mobile/packages/features/staff/authentication/lib/src/data/repositories_impl/place_repository_impl.dart new file mode 100644 index 00000000..9f8e99ad --- /dev/null +++ b/apps/mobile/packages/features/staff/authentication/lib/src/data/repositories_impl/place_repository_impl.dart @@ -0,0 +1,49 @@ +import 'dart:convert'; +import 'package:http/http.dart' as http; +import 'package:krow_core/core.dart'; +import '../../domain/repositories/place_repository.dart'; + +class PlaceRepositoryImpl implements PlaceRepository { + final http.Client _client; + + PlaceRepositoryImpl({http.Client? client}) : _client = client ?? http.Client(); + + @override + Future> searchCities(String query) async { + if (query.isEmpty) return []; + + final Uri uri = Uri.https( + 'maps.googleapis.com', + '/maps/api/place/autocomplete/json', + { + 'input': query, + 'types': '(cities)', + 'key': AppConfig.googlePlacesApiKey, + }, + ); + + try { + final http.Response response = await _client.get(uri); + + if (response.statusCode == 200) { + final Map data = json.decode(response.body) as Map; + + if (data['status'] == 'OK' || data['status'] == 'ZERO_RESULTS') { + final List predictions = data['predictions'] as List; + + return predictions.map((dynamic prediction) { + return prediction['description'] as String; + }).toList(); + } else { + // Handle other statuses (OVER_QUERY_LIMIT, REQUEST_DENIED, etc.) + // Returning empty list for now to avoid crashing UI, ideally log this. + return []; + } + } else { + throw Exception('Network Error: ${response.statusCode}'); + } + } catch (e) { + rethrow; + } + } +} diff --git a/apps/mobile/packages/features/staff/authentication/lib/src/domain/repositories/place_repository.dart b/apps/mobile/packages/features/staff/authentication/lib/src/domain/repositories/place_repository.dart new file mode 100644 index 00000000..d241cd00 --- /dev/null +++ b/apps/mobile/packages/features/staff/authentication/lib/src/domain/repositories/place_repository.dart @@ -0,0 +1,5 @@ +abstract class PlaceRepository { + /// Searches for cities matching the [query]. + /// Returns a list of city names. + Future> searchCities(String query); +} diff --git a/apps/mobile/packages/features/staff/authentication/lib/src/domain/usecases/search_cities_usecase.dart b/apps/mobile/packages/features/staff/authentication/lib/src/domain/usecases/search_cities_usecase.dart new file mode 100644 index 00000000..def8c3ca --- /dev/null +++ b/apps/mobile/packages/features/staff/authentication/lib/src/domain/usecases/search_cities_usecase.dart @@ -0,0 +1,11 @@ +import '../repositories/place_repository.dart'; + +class SearchCitiesUseCase { + final PlaceRepository _repository; + + SearchCitiesUseCase(this._repository); + + Future> call(String query) { + return _repository.searchCities(query); + } +} diff --git a/apps/mobile/packages/features/staff/authentication/lib/src/presentation/blocs/profile_setup/profile_setup_bloc.dart b/apps/mobile/packages/features/staff/authentication/lib/src/presentation/blocs/profile_setup/profile_setup_bloc.dart index 324ea906..6d8d80b3 100644 --- a/apps/mobile/packages/features/staff/authentication/lib/src/presentation/blocs/profile_setup/profile_setup_bloc.dart +++ b/apps/mobile/packages/features/staff/authentication/lib/src/presentation/blocs/profile_setup/profile_setup_bloc.dart @@ -1,6 +1,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../domain/usecases/submit_profile_setup_usecase.dart'; +import '../../../domain/usecases/search_cities_usecase.dart'; + import 'profile_setup_event.dart'; import 'profile_setup_state.dart'; @@ -11,7 +13,9 @@ export 'profile_setup_state.dart'; class ProfileSetupBloc extends Bloc { ProfileSetupBloc({ required SubmitProfileSetup submitProfileSetup, + required SearchCitiesUseCase searchCities, }) : _submitProfileSetup = submitProfileSetup, + _searchCities = searchCities, super(const ProfileSetupState()) { on(_onFullNameChanged); on(_onBioChanged); @@ -20,9 +24,12 @@ class ProfileSetupBloc extends Bloc { on(_onSkillsChanged); on(_onIndustriesChanged); on(_onSubmitted); + on(_onLocationQueryChanged); + on(_onClearLocationSuggestions); } final SubmitProfileSetup _submitProfileSetup; + final SearchCitiesUseCase _searchCities; /// Handles the [ProfileSetupFullNameChanged] event. void _onFullNameChanged( @@ -99,4 +106,29 @@ class ProfileSetupBloc extends Bloc { ); } } + + Future _onLocationQueryChanged( + ProfileSetupLocationQueryChanged event, + Emitter emit, + ) async { + if (event.query.isEmpty) { + emit(state.copyWith(locationSuggestions: [])); + return; + } + + try { + final results = await _searchCities(event.query); + emit(state.copyWith(locationSuggestions: results)); + } catch (e) { + // Quietly fail or clear + emit(state.copyWith(locationSuggestions: [])); + } + } + + void _onClearLocationSuggestions( + ProfileSetupClearLocationSuggestions event, + Emitter emit, + ) { + emit(state.copyWith(locationSuggestions: [])); + } } diff --git a/apps/mobile/packages/features/staff/authentication/lib/src/presentation/blocs/profile_setup/profile_setup_event.dart b/apps/mobile/packages/features/staff/authentication/lib/src/presentation/blocs/profile_setup/profile_setup_event.dart index 39fac246..b628f342 100644 --- a/apps/mobile/packages/features/staff/authentication/lib/src/presentation/blocs/profile_setup/profile_setup_event.dart +++ b/apps/mobile/packages/features/staff/authentication/lib/src/presentation/blocs/profile_setup/profile_setup_event.dart @@ -80,6 +80,24 @@ class ProfileSetupIndustriesChanged extends ProfileSetupEvent { List get props => [industries]; } +/// Event triggered when the location query changes. +class ProfileSetupLocationQueryChanged extends ProfileSetupEvent { + /// The search query. + final String query; + + /// Creates a [ProfileSetupLocationQueryChanged] event. + const ProfileSetupLocationQueryChanged(this.query); + + @override + List get props => [query]; +} + +/// Event triggered when the location suggestions should be cleared. +class ProfileSetupClearLocationSuggestions extends ProfileSetupEvent { + /// Creates a [ProfileSetupClearLocationSuggestions] event. + const ProfileSetupClearLocationSuggestions(); +} + /// Event triggered when the profile submission is requested. class ProfileSetupSubmitted extends ProfileSetupEvent { /// Creates a [ProfileSetupSubmitted] event. diff --git a/apps/mobile/packages/features/staff/authentication/lib/src/presentation/blocs/profile_setup/profile_setup_state.dart b/apps/mobile/packages/features/staff/authentication/lib/src/presentation/blocs/profile_setup/profile_setup_state.dart index 2406d5c8..952c9153 100644 --- a/apps/mobile/packages/features/staff/authentication/lib/src/presentation/blocs/profile_setup/profile_setup_state.dart +++ b/apps/mobile/packages/features/staff/authentication/lib/src/presentation/blocs/profile_setup/profile_setup_state.dart @@ -26,8 +26,8 @@ class ProfileSetupState extends Equatable { /// The current status of the profile setup process. final ProfileSetupStatus status; - /// Error message if the status is [ProfileSetupStatus.failure]. - final String? errorMessage; + /// List of location suggestions from the API. + final List locationSuggestions; /// Creates a [ProfileSetupState] instance. const ProfileSetupState({ @@ -39,6 +39,7 @@ class ProfileSetupState extends Equatable { this.industries = const [], this.status = ProfileSetupStatus.initial, this.errorMessage, + this.locationSuggestions = const [], }); /// Creates a copy of the current state with updated values. @@ -51,6 +52,7 @@ class ProfileSetupState extends Equatable { List? industries, ProfileSetupStatus? status, String? errorMessage, + List? locationSuggestions, }) { return ProfileSetupState( fullName: fullName ?? this.fullName, @@ -61,18 +63,20 @@ class ProfileSetupState extends Equatable { industries: industries ?? this.industries, status: status ?? this.status, errorMessage: errorMessage, + locationSuggestions: locationSuggestions ?? this.locationSuggestions, ); } @override List get props => [ - fullName, - bio, - preferredLocations, - maxDistanceMiles, - skills, - industries, - status, - errorMessage, - ]; + fullName, + bio, + preferredLocations, + maxDistanceMiles, + skills, + industries, + status, + errorMessage, + locationSuggestions, + ]; } diff --git a/apps/mobile/packages/features/staff/authentication/lib/src/presentation/widgets/profile_setup_page/profile_setup_location.dart b/apps/mobile/packages/features/staff/authentication/lib/src/presentation/widgets/profile_setup_page/profile_setup_location.dart index b62b953a..34d8819a 100644 --- a/apps/mobile/packages/features/staff/authentication/lib/src/presentation/widgets/profile_setup_page/profile_setup_location.dart +++ b/apps/mobile/packages/features/staff/authentication/lib/src/presentation/widgets/profile_setup_page/profile_setup_location.dart @@ -1,5 +1,7 @@ +import 'dart:async'; import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:staff_authentication/src/presentation/widgets/common/section_title_subtitle.dart'; import 'package:staff_authentication/staff_authentication.dart'; @@ -32,26 +34,38 @@ class ProfileSetupLocation extends StatefulWidget { class _ProfileSetupLocationState extends State { final TextEditingController _locationController = TextEditingController(); + Timer? _debounce; @override void dispose() { _locationController.dispose(); + _debounce?.cancel(); super.dispose(); } - /// Adds the current text from the controller as a location. - void _addLocation() { - final String loc = _locationController.text.trim(); - if (loc.isNotEmpty && !widget.preferredLocations.contains(loc)) { - final List updatedList = List.from(widget.preferredLocations) - ..add(loc); + void _onSearchChanged(String query) { + if (_debounce?.isActive ?? false) _debounce!.cancel(); + _debounce = Timer(const Duration(milliseconds: 300), () { + context + .read() + .add(ProfileSetupLocationQueryChanged(query)); + }); + } + + /// Adds the selected location. + void _addLocation(String location) { + if (location.isNotEmpty && !widget.preferredLocations.contains(location)) { + final List updatedList = + List.from(widget.preferredLocations)..add(location); widget.onLocationsChanged(updatedList); _locationController.clear(); + context + .read() + .add(const ProfileSetupClearLocationSuggestions()); } } @override - /// Builds the location setup step UI. Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -62,37 +76,55 @@ class _ProfileSetupLocationState extends State { ), const SizedBox(height: UiConstants.space8), - // Add Location input - Row( - crossAxisAlignment: CrossAxisAlignment.end, - spacing: UiConstants.space2, - children: [ - Expanded( - child: UiTextField( - label: t - .staff_authentication - .profile_setup_page - .location - .add_location_label, - controller: _locationController, - hintText: t - .staff_authentication - .profile_setup_page - .location - .add_location_hint, - onSubmitted: (_) => _addLocation(), + // Search Input + UiTextField( + label: t.staff_authentication.profile_setup_page.location + .add_location_label, + controller: _locationController, + hintText: t.staff_authentication.profile_setup_page.location + .add_location_hint, + onChanged: _onSearchChanged, + ), + + // Suggestions List + BlocBuilder( + buildWhen: (previous, current) => + previous.locationSuggestions != current.locationSuggestions, + builder: (context, state) { + if (state.locationSuggestions.isEmpty) { + return const SizedBox.shrink(); + } + return Container( + constraints: const BoxConstraints(maxHeight: 200), + margin: const EdgeInsets.only(top: UiConstants.space2), + decoration: BoxDecoration( + color: Theme.of(context).cardColor, + borderRadius: BorderRadius.circular(UiConstants.radiusMd), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], ), - ), - UiButton.secondary( - text: - t.staff_authentication.profile_setup_page.location.add_button, - onPressed: _addLocation, - style: OutlinedButton.styleFrom( - minimumSize: const Size(0, 48), - maximumSize: const Size(double.infinity, 48), + child: ListView.separated( + shrinkWrap: true, + padding: EdgeInsets.zero, + itemCount: state.locationSuggestions.length, + separatorBuilder: (context, index) => const Divider(height: 1), + itemBuilder: (context, index) { + final suggestion = state.locationSuggestions[index]; + return ListTile( + title: Text(suggestion, style: UiTypography.body2m), + leading: const Icon(UiIcons.mapPin, size: 16), + onTap: () => _addLocation(suggestion), + visualDensity: VisualDensity.compact, + ); + }, ), - ), - ], + ); + }, ), const SizedBox(height: UiConstants.space4), @@ -134,18 +166,12 @@ class _ProfileSetupLocationState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - t - .staff_authentication - .profile_setup_page - .location + t.staff_authentication.profile_setup_page.location .min_dist_label, style: UiTypography.footnote1r.textSecondary, ), Text( - t - .staff_authentication - .profile_setup_page - .location + t.staff_authentication.profile_setup_page.location .max_dist_label, style: UiTypography.footnote1r.textSecondary, ), @@ -158,8 +184,8 @@ class _ProfileSetupLocationState extends State { /// Removes the specified [location] from the list. void _removeLocation({required String location}) { - final List updatedList = List.from(widget.preferredLocations) - ..remove(location); + final List updatedList = + List.from(widget.preferredLocations)..remove(location); widget.onLocationsChanged(updatedList); } } diff --git a/apps/mobile/packages/features/staff/authentication/lib/staff_authentication.dart b/apps/mobile/packages/features/staff/authentication/lib/staff_authentication.dart index b98c5356..d4c2a5fd 100644 --- a/apps/mobile/packages/features/staff/authentication/lib/staff_authentication.dart +++ b/apps/mobile/packages/features/staff/authentication/lib/staff_authentication.dart @@ -11,6 +11,9 @@ import 'package:staff_authentication/src/domain/usecases/verify_otp_usecase.dart import 'package:staff_authentication/src/domain/repositories/profile_setup_repository.dart'; import 'package:staff_authentication/src/data/repositories_impl/profile_setup_repository_impl.dart'; import 'package:staff_authentication/src/domain/usecases/submit_profile_setup_usecase.dart'; +import 'package:staff_authentication/src/domain/repositories/place_repository.dart'; +import 'package:staff_authentication/src/data/repositories_impl/place_repository_impl.dart'; +import 'package:staff_authentication/src/domain/usecases/search_cities_usecase.dart'; import 'package:staff_authentication/src/presentation/blocs/auth_bloc.dart'; import 'package:staff_authentication/src/presentation/blocs/profile_setup/profile_setup_bloc.dart'; import 'package:staff_authentication/src/presentation/pages/get_started_page.dart'; @@ -44,11 +47,13 @@ class StaffAuthenticationModule extends Module { dataConnect: ExampleConnector.instance, ), ); + i.addLazySingleton(PlaceRepositoryImpl.new); // UseCases i.addLazySingleton(SignInWithPhoneUseCase.new); i.addLazySingleton(VerifyOtpUseCase.new); i.addLazySingleton(SubmitProfileSetup.new); + i.addLazySingleton(SearchCitiesUseCase.new); // BLoCs i.addLazySingleton( @@ -60,6 +65,7 @@ class StaffAuthenticationModule extends Module { i.add( () => ProfileSetupBloc( submitProfileSetup: i.get(), + searchCities: i.get(), ), ); } diff --git a/apps/mobile/packages/features/staff/authentication/pubspec.yaml b/apps/mobile/packages/features/staff/authentication/pubspec.yaml index 87b79949..6a955e2e 100644 --- a/apps/mobile/packages/features/staff/authentication/pubspec.yaml +++ b/apps/mobile/packages/features/staff/authentication/pubspec.yaml @@ -18,6 +18,7 @@ dependencies: firebase_core: ^4.2.1 firebase_auth: ^6.1.2 # Updated for compatibility firebase_data_connect: ^0.2.2+1 + http: ^1.2.0 # Architecture Packages krow_domain: