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.
This commit is contained in:
2026-02-04 12:30:20 +05:30
parent 41b808d196
commit 1ba83e3ea6
15 changed files with 303 additions and 76 deletions

View File

@@ -255,20 +255,12 @@ class ViewOrdersCubit extends Cubit<ViewOrdersState> {
}
int _calculateCategoryCount(String category) {
if (state.selectedDate == null) return 0;
final String selectedDateStr = DateFormat(
'yyyy-MM-dd',
).format(state.selectedDate!);
final List<OrderItem> 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<ViewOrdersState> {
}
int _calculateUpNextCount() {
if (state.selectedDate == null) return 0;
final String selectedDateStr = DateFormat(
'yyyy-MM-dd',
).format(state.selectedDate!);
final List<OrderItem> 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.

View File

@@ -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<ViewOrdersCubit, ViewOrdersState>(
'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');
},
);
});
}