feat: Implement UI for adding new shift manager during order creation

This commit is contained in:
Achintha Isuru
2026-03-10 11:46:11 -04:00
parent 53578a4e0d
commit 1f79541404
11 changed files with 393 additions and 185 deletions

View File

@@ -1,10 +1,12 @@
// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs
import 'dart:convert';
import 'package:firebase_data_connect/src/core/ref.dart';
import 'package:firebase_data_connect/firebase_data_connect.dart';
import 'package:http/http.dart' as http;
import 'package:krow_core/core.dart';
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
import 'package:krow_domain/krow_domain.dart';
import '../../domain/repositories/hubs_connector_repository.dart';
/// Implementation of [HubsConnectorRepository].

View File

@@ -4,7 +4,9 @@ import 'package:krow_core/core.dart';
import 'package:krow_data_connect/krow_data_connect.dart';
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
import 'data/repositories_impl/client_create_order_repository_impl.dart';
import 'data/repositories_impl/client_order_query_repository_impl.dart';
import 'domain/repositories/client_create_order_repository_interface.dart';
import 'domain/repositories/client_order_query_repository_interface.dart';
import 'domain/usecases/create_one_time_order_usecase.dart';
import 'domain/usecases/create_permanent_order_usecase.dart';
import 'domain/usecases/create_recurring_order_usecase.dart';
@@ -40,6 +42,12 @@ class ClientCreateOrderModule extends Module {
),
);
i.addLazySingleton<ClientOrderQueryRepositoryInterface>(
() => ClientOrderQueryRepositoryImpl(
service: i.get<dc.DataConnectService>(),
),
);
// UseCases
i.addLazySingleton(CreateOneTimeOrderUseCase.new);
i.addLazySingleton(CreatePermanentOrderUseCase.new);
@@ -58,7 +66,13 @@ class ClientCreateOrderModule extends Module {
),
);
i.add<OneTimeOrderBloc>(OneTimeOrderBloc.new);
i.add<PermanentOrderBloc>(PermanentOrderBloc.new);
i.add<PermanentOrderBloc>(
() => PermanentOrderBloc(
i.get<CreatePermanentOrderUseCase>(),
i.get<GetOrderDetailsForReorderUseCase>(),
i.get<ClientOrderQueryRepositoryInterface>(),
),
);
i.add<RecurringOrderBloc>(RecurringOrderBloc.new);
}

View File

@@ -0,0 +1,107 @@
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
import 'package:krow_domain/krow_domain.dart';
import '../../domain/models/order_hub.dart';
import '../../domain/models/order_manager.dart';
import '../../domain/models/order_role.dart';
import '../../domain/repositories/client_order_query_repository_interface.dart';
/// Data layer implementation of [ClientOrderQueryRepositoryInterface].
///
/// Delegates all backend calls to [dc.DataConnectService] using the
/// `_service.run()` pattern for automatic auth validation, token refresh,
/// and retry logic. Each method maps Data Connect response types to the
/// corresponding clean domain models.
class ClientOrderQueryRepositoryImpl
implements ClientOrderQueryRepositoryInterface {
/// Creates an instance backed by the given [service].
ClientOrderQueryRepositoryImpl({required dc.DataConnectService service})
: _service = service;
final dc.DataConnectService _service;
@override
Future<List<Vendor>> getVendors() async {
return _service.run(() async {
final result = await _service.connector.listVendors().execute();
return result.data.vendors
.map(
(dc.ListVendorsVendors vendor) => Vendor(
id: vendor.id,
name: vendor.companyName,
rates: const <String, double>{},
),
)
.toList();
});
}
@override
Future<List<OrderRole>> getRolesByVendor(String vendorId) async {
return _service.run(() async {
final result = await _service.connector
.listRolesByVendorId(vendorId: vendorId)
.execute();
return result.data.roles
.map(
(dc.ListRolesByVendorIdRoles role) => OrderRole(
id: role.id,
name: role.name,
costPerHour: role.costPerHour,
),
)
.toList();
});
}
@override
Future<List<OrderHub>> getHubsByOwner(String ownerId) async {
return _service.run(() async {
final result = await _service.connector
.listTeamHubsByOwnerId(ownerId: ownerId)
.execute();
return result.data.teamHubs
.map(
(dc.ListTeamHubsByOwnerIdTeamHubs hub) => OrderHub(
id: hub.id,
name: hub.hubName,
address: hub.address,
placeId: hub.placeId,
latitude: hub.latitude,
longitude: hub.longitude,
city: hub.city,
state: hub.state,
street: hub.street,
country: hub.country,
zipCode: hub.zipCode,
),
)
.toList();
});
}
@override
Future<List<OrderManager>> getManagersByHub(String hubId) async {
return _service.run(() async {
final result = await _service.connector.listTeamMembers().execute();
return result.data.teamMembers
.where(
(dc.ListTeamMembersTeamMembers member) =>
member.teamHubId == hubId &&
member.role is dc.Known<dc.TeamMemberRole> &&
(member.role as dc.Known<dc.TeamMemberRole>).value ==
dc.TeamMemberRole.MANAGER,
)
.map(
(dc.ListTeamMembersTeamMembers member) => OrderManager(
id: member.id,
name: member.user.fullName ?? 'Unknown',
),
)
.toList();
});
}
@override
Future<String> getBusinessId() => _service.getBusinessId();
}

View File

@@ -0,0 +1,72 @@
import 'package:equatable/equatable.dart';
/// A team hub (location) available for order assignment.
///
/// This domain model represents a physical hub location owned by the business.
/// It is used to populate hub selection dropdowns and to attach location
/// details when creating shifts for an order.
class OrderHub extends Equatable {
/// Creates an [OrderHub] with the required [id], [name], and [address],
/// plus optional geo-location and address component fields.
const OrderHub({
required this.id,
required this.name,
required this.address,
this.placeId,
this.latitude,
this.longitude,
this.city,
this.state,
this.street,
this.country,
this.zipCode,
});
/// Unique identifier of the hub.
final String id;
/// Human-readable display name of the hub.
final String name;
/// Full street address of the hub.
final String address;
/// Google Places ID, if available.
final String? placeId;
/// Geographic latitude of the hub.
final double? latitude;
/// Geographic longitude of the hub.
final double? longitude;
/// City where the hub is located.
final String? city;
/// State or province where the hub is located.
final String? state;
/// Street name portion of the address.
final String? street;
/// Country where the hub is located.
final String? country;
/// Postal / ZIP code of the hub.
final String? zipCode;
@override
List<Object?> get props => <Object?>[
id,
name,
address,
placeId,
latitude,
longitude,
city,
state,
street,
country,
zipCode,
];
}

View File

@@ -0,0 +1,20 @@
import 'package:equatable/equatable.dart';
/// A hub manager available for assignment to an order.
///
/// This domain model represents a team member with a MANAGER role at a
/// specific hub. It is used to populate the manager selection dropdown
/// when creating or editing an order.
class OrderManager extends Equatable {
/// Creates an [OrderManager] with the given [id] and [name].
const OrderManager({required this.id, required this.name});
/// Unique identifier of the manager (team member ID).
final String id;
/// Full display name of the manager.
final String name;
@override
List<Object?> get props => <Object?>[id, name];
}

View File

@@ -0,0 +1,28 @@
import 'package:equatable/equatable.dart';
/// A role available for staffing positions within an order.
///
/// This domain model represents a staffing role fetched from the backend,
/// decoupled from any data layer dependencies. It carries the role identity
/// and its hourly cost so the presentation layer can populate dropdowns
/// and calculate estimates.
class OrderRole extends Equatable {
/// Creates an [OrderRole] with the given [id], [name], and [costPerHour].
const OrderRole({
required this.id,
required this.name,
required this.costPerHour,
});
/// Unique identifier of the role.
final String id;
/// Human-readable display name of the role.
final String name;
/// Hourly cost rate for this role.
final double costPerHour;
@override
List<Object?> get props => <Object?>[id, name, costPerHour];
}

View File

@@ -0,0 +1,39 @@
import 'package:krow_domain/krow_domain.dart';
import '../models/order_hub.dart';
import '../models/order_manager.dart';
import '../models/order_role.dart';
/// Interface for querying order-related reference data.
///
/// This repository centralises the read-only queries that the order creation
/// BLoCs need (vendors, roles, hubs, managers) so that they no longer depend
/// directly on [DataConnectService] or the `krow_data_connect` package.
///
/// Implementations live in the data layer and translate backend responses
/// into clean domain models.
abstract interface class ClientOrderQueryRepositoryInterface {
/// Returns the list of available vendors.
///
/// The returned [Vendor] objects come from the shared `krow_domain` package
/// because `Vendor` is already a clean domain entity.
Future<List<Vendor>> getVendors();
/// Returns the roles offered by the vendor identified by [vendorId].
Future<List<OrderRole>> getRolesByVendor(String vendorId);
/// Returns the team hubs owned by the business identified by [ownerId].
Future<List<OrderHub>> getHubsByOwner(String ownerId);
/// Returns the managers assigned to the hub identified by [hubId].
///
/// Only team members with the MANAGER role at the given hub are included.
Future<List<OrderManager>> getManagersByHub(String hubId);
/// Returns the current business ID from the active client session.
///
/// This allows BLoCs to resolve the business ID without depending on
/// the data layer's session store directly, keeping the presentation
/// layer free from `krow_data_connect` imports.
Future<String> getBusinessId();
}

View File

@@ -1,10 +1,12 @@
import 'package:client_create_order/src/domain/arguments/one_time_order_arguments.dart';
import 'package:client_create_order/src/domain/models/order_hub.dart';
import 'package:client_create_order/src/domain/models/order_manager.dart';
import 'package:client_create_order/src/domain/models/order_role.dart';
import 'package:client_create_order/src/domain/repositories/client_order_query_repository_interface.dart';
import 'package:client_create_order/src/domain/usecases/create_one_time_order_usecase.dart';
import 'package:client_create_order/src/domain/usecases/get_order_details_for_reorder_usecase.dart';
import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc;
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow_core/core.dart';
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
import 'package:krow_domain/krow_domain.dart';
import 'one_time_order_event.dart';
@@ -18,7 +20,7 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
OneTimeOrderBloc(
this._createOneTimeOrderUseCase,
this._getOrderDetailsForReorderUseCase,
this._service,
this._queryRepository,
) : super(OneTimeOrderState.initial()) {
on<OneTimeOrderVendorsLoaded>(_onVendorsLoaded);
on<OneTimeOrderVendorChanged>(_onVendorChanged);
@@ -39,25 +41,11 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
}
final CreateOneTimeOrderUseCase _createOneTimeOrderUseCase;
final GetOrderDetailsForReorderUseCase _getOrderDetailsForReorderUseCase;
final dc.DataConnectService _service;
final ClientOrderQueryRepositoryInterface _queryRepository;
Future<void> _loadVendors() async {
final List<Vendor>? vendors = await handleErrorWithResult(
action: () async {
final fdc.QueryResult<dc.ListVendorsData, void> result = await _service
.connector
.listVendors()
.execute();
return result.data.vendors
.map(
(dc.ListVendorsVendors vendor) => Vendor(
id: vendor.id,
name: vendor.companyName,
rates: const <String, double>{},
),
)
.toList();
},
action: () => _queryRepository.getVendors(),
onError: (_) => add(const OneTimeOrderVendorsLoaded(<Vendor>[])),
);
@@ -72,19 +60,14 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
) async {
final List<OneTimeOrderRoleOption>? roles = await handleErrorWithResult(
action: () async {
final fdc.QueryResult<
dc.ListRolesByVendorIdData,
dc.ListRolesByVendorIdVariables
>
result = await _service.connector
.listRolesByVendorId(vendorId: vendorId)
.execute();
return result.data.roles
final List<OrderRole> result =
await _queryRepository.getRolesByVendor(vendorId);
return result
.map(
(dc.ListRolesByVendorIdRoles role) => OneTimeOrderRoleOption(
id: role.id,
name: role.name,
costPerHour: role.costPerHour,
(OrderRole r) => OneTimeOrderRoleOption(
id: r.id,
name: r.name,
costPerHour: r.costPerHour,
),
)
.toList();
@@ -101,28 +84,23 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
Future<void> _loadHubs() async {
final List<OneTimeOrderHubOption>? hubs = await handleErrorWithResult(
action: () async {
final String businessId = await _service.getBusinessId();
final fdc.QueryResult<
dc.ListTeamHubsByOwnerIdData,
dc.ListTeamHubsByOwnerIdVariables
>
result = await _service.connector
.listTeamHubsByOwnerId(ownerId: businessId)
.execute();
return result.data.teamHubs
final String businessId = await _queryRepository.getBusinessId();
final List<OrderHub> result =
await _queryRepository.getHubsByOwner(businessId);
return result
.map(
(dc.ListTeamHubsByOwnerIdTeamHubs hub) => OneTimeOrderHubOption(
id: hub.id,
name: hub.hubName,
address: hub.address,
placeId: hub.placeId,
latitude: hub.latitude,
longitude: hub.longitude,
city: hub.city,
state: hub.state,
street: hub.street,
country: hub.country,
zipCode: hub.zipCode,
(OrderHub h) => OneTimeOrderHubOption(
id: h.id,
name: h.name,
address: h.address,
placeId: h.placeId,
latitude: h.latitude,
longitude: h.longitude,
city: h.city,
state: h.state,
street: h.street,
country: h.country,
zipCode: h.zipCode,
),
)
.toList();
@@ -140,23 +118,14 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
final List<OneTimeOrderManagerOption>? managers =
await handleErrorWithResult(
action: () async {
final fdc.QueryResult<dc.ListTeamMembersData, void> result =
await _service.connector.listTeamMembers().execute();
return result.data.teamMembers
.where(
(dc.ListTeamMembersTeamMembers member) =>
member.teamHubId == hubId &&
member.role is dc.Known<dc.TeamMemberRole> &&
(member.role as dc.Known<dc.TeamMemberRole>).value ==
dc.TeamMemberRole.MANAGER,
)
final List<OrderManager> result =
await _queryRepository.getManagersByHub(hubId);
return result
.map(
(dc.ListTeamMembersTeamMembers member) =>
OneTimeOrderManagerOption(
id: member.id,
name: member.user.fullName ?? 'Unknown',
),
(OrderManager m) => OneTimeOrderManagerOption(
id: m.id,
name: m.name,
),
)
.toList();
},

View File

@@ -1,9 +1,11 @@
import 'package:client_create_order/src/domain/models/order_hub.dart';
import 'package:client_create_order/src/domain/models/order_manager.dart';
import 'package:client_create_order/src/domain/models/order_role.dart';
import 'package:client_create_order/src/domain/repositories/client_order_query_repository_interface.dart';
import 'package:client_create_order/src/domain/usecases/create_permanent_order_usecase.dart';
import 'package:client_create_order/src/domain/usecases/get_order_details_for_reorder_usecase.dart';
import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc;
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow_core/core.dart';
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
import 'package:krow_domain/krow_domain.dart' as domain;
import 'permanent_order_event.dart';
@@ -17,7 +19,7 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
PermanentOrderBloc(
this._createPermanentOrderUseCase,
this._getOrderDetailsForReorderUseCase,
this._service,
this._queryRepository,
) : super(PermanentOrderState.initial()) {
on<PermanentOrderVendorsLoaded>(_onVendorsLoaded);
on<PermanentOrderVendorChanged>(_onVendorChanged);
@@ -40,7 +42,7 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
final CreatePermanentOrderUseCase _createPermanentOrderUseCase;
final GetOrderDetailsForReorderUseCase _getOrderDetailsForReorderUseCase;
final dc.DataConnectService _service;
final ClientOrderQueryRepositoryInterface _queryRepository;
static const List<String> _dayLabels = <String>[
'SUN',
@@ -54,21 +56,7 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
Future<void> _loadVendors() async {
final List<domain.Vendor>? vendors = await handleErrorWithResult(
action: () async {
final fdc.QueryResult<dc.ListVendorsData, void> result = await _service
.connector
.listVendors()
.execute();
return result.data.vendors
.map(
(dc.ListVendorsVendors vendor) => domain.Vendor(
id: vendor.id,
name: vendor.companyName,
rates: const <String, double>{},
),
)
.toList();
},
action: () => _queryRepository.getVendors(),
onError: (_) => add(const PermanentOrderVendorsLoaded(<domain.Vendor>[])),
);
@@ -83,19 +71,14 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
) async {
final List<PermanentOrderRoleOption>? roles = await handleErrorWithResult(
action: () async {
final fdc.QueryResult<
dc.ListRolesByVendorIdData,
dc.ListRolesByVendorIdVariables
>
result = await _service.connector
.listRolesByVendorId(vendorId: vendorId)
.execute();
return result.data.roles
final List<OrderRole> orderRoles =
await _queryRepository.getRolesByVendor(vendorId);
return orderRoles
.map(
(dc.ListRolesByVendorIdRoles role) => PermanentOrderRoleOption(
id: role.id,
name: role.name,
costPerHour: role.costPerHour,
(OrderRole r) => PermanentOrderRoleOption(
id: r.id,
name: r.name,
costPerHour: r.costPerHour,
),
)
.toList();
@@ -112,19 +95,17 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
Future<void> _loadHubs() async {
final List<PermanentOrderHubOption>? hubs = await handleErrorWithResult(
action: () async {
final String businessId = await _service.getBusinessId();
final fdc.QueryResult<
dc.ListTeamHubsByOwnerIdData,
dc.ListTeamHubsByOwnerIdVariables
>
result = await _service.connector
.listTeamHubsByOwnerId(ownerId: businessId)
.execute();
return result.data.teamHubs
final String? businessId = await _queryRepository.getBusinessId();
if (businessId == null || businessId.isEmpty) {
return <PermanentOrderHubOption>[];
}
final List<OrderHub> orderHubs =
await _queryRepository.getHubsByOwner(businessId);
return orderHubs
.map(
(dc.ListTeamHubsByOwnerIdTeamHubs hub) => PermanentOrderHubOption(
(OrderHub hub) => PermanentOrderHubOption(
id: hub.id,
name: hub.hubName,
name: hub.name,
address: hub.address,
placeId: hub.placeId,
latitude: hub.latitude,
@@ -219,22 +200,13 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
final List<PermanentOrderManagerOption>? managers =
await handleErrorWithResult(
action: () async {
final fdc.QueryResult<dc.ListTeamMembersData, void> result =
await _service.connector.listTeamMembers().execute();
return result.data.teamMembers
.where(
(dc.ListTeamMembersTeamMembers member) =>
member.teamHubId == hubId &&
member.role is dc.Known<dc.TeamMemberRole> &&
(member.role as dc.Known<dc.TeamMemberRole>).value ==
dc.TeamMemberRole.MANAGER,
)
final List<OrderManager> orderManagers =
await _queryRepository.getManagersByHub(hubId);
return orderManagers
.map(
(dc.ListTeamMembersTeamMembers member) =>
PermanentOrderManagerOption(
id: member.id,
name: member.user.fullName ?? 'Unknown',
(OrderManager m) => PermanentOrderManagerOption(
id: m.id,
name: m.name,
),
)
.toList();

View File

@@ -1,23 +1,32 @@
import 'package:client_create_order/src/domain/models/order_hub.dart';
import 'package:client_create_order/src/domain/models/order_manager.dart';
import 'package:client_create_order/src/domain/models/order_role.dart';
import 'package:client_create_order/src/domain/repositories/client_order_query_repository_interface.dart';
import 'package:client_create_order/src/domain/usecases/create_recurring_order_usecase.dart';
import 'package:client_create_order/src/domain/usecases/get_order_details_for_reorder_usecase.dart';
import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc;
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow_core/core.dart';
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
import 'package:krow_domain/krow_domain.dart' as domain;
import 'recurring_order_event.dart';
import 'recurring_order_state.dart';
/// BLoC for managing the recurring order creation form.
///
/// This BLoC delegates all backend queries to
/// [ClientOrderQueryRepositoryInterface] and order submission to
/// [CreateRecurringOrderUseCase], keeping the presentation layer free
/// from direct `krow_data_connect` imports.
class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
with
BlocErrorHandler<RecurringOrderState>,
SafeBloc<RecurringOrderEvent, RecurringOrderState> {
/// Creates a [RecurringOrderBloc] with the required use cases and
/// query repository.
RecurringOrderBloc(
this._createRecurringOrderUseCase,
this._getOrderDetailsForReorderUseCase,
this._service,
this._queryRepository,
) : super(RecurringOrderState.initial()) {
on<RecurringOrderVendorsLoaded>(_onVendorsLoaded);
on<RecurringOrderVendorChanged>(_onVendorChanged);
@@ -41,7 +50,7 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
final CreateRecurringOrderUseCase _createRecurringOrderUseCase;
final GetOrderDetailsForReorderUseCase _getOrderDetailsForReorderUseCase;
final dc.DataConnectService _service;
final ClientOrderQueryRepositoryInterface _queryRepository;
static const List<String> _dayLabels = <String>[
'SUN',
@@ -53,24 +62,14 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
'SAT',
];
/// Loads the list of available vendors from the query repository.
Future<void> _loadVendors() async {
final List<domain.Vendor>? vendors = await handleErrorWithResult(
action: () async {
final fdc.QueryResult<dc.ListVendorsData, void> result = await _service
.connector
.listVendors()
.execute();
return result.data.vendors
.map(
(dc.ListVendorsVendors vendor) => domain.Vendor(
id: vendor.id,
name: vendor.companyName,
rates: const <String, double>{},
),
)
.toList();
return _queryRepository.getVendors();
},
onError: (_) => add(const RecurringOrderVendorsLoaded(<domain.Vendor>[])),
onError: (_) =>
add(const RecurringOrderVendorsLoaded(<domain.Vendor>[])),
);
if (vendors != null) {
@@ -78,25 +77,22 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
}
}
/// Loads roles for the given [vendorId] and maps them to presentation
/// option models.
Future<void> _loadRolesForVendor(
String vendorId,
Emitter<RecurringOrderState> emit,
) async {
final List<RecurringOrderRoleOption>? roles = await handleErrorWithResult(
action: () async {
final fdc.QueryResult<
dc.ListRolesByVendorIdData,
dc.ListRolesByVendorIdVariables
>
result = await _service.connector
.listRolesByVendorId(vendorId: vendorId)
.execute();
return result.data.roles
final List<OrderRole> orderRoles =
await _queryRepository.getRolesByVendor(vendorId);
return orderRoles
.map(
(dc.ListRolesByVendorIdRoles role) => RecurringOrderRoleOption(
id: role.id,
name: role.name,
costPerHour: role.costPerHour,
(OrderRole r) => RecurringOrderRoleOption(
id: r.id,
name: r.name,
costPerHour: r.costPerHour,
),
)
.toList();
@@ -110,22 +106,19 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
}
}
/// Loads team hubs for the current business owner and maps them to
/// presentation option models.
Future<void> _loadHubs() async {
final List<RecurringOrderHubOption>? hubs = await handleErrorWithResult(
action: () async {
final String businessId = await _service.getBusinessId();
final fdc.QueryResult<
dc.ListTeamHubsByOwnerIdData,
dc.ListTeamHubsByOwnerIdVariables
>
result = await _service.connector
.listTeamHubsByOwnerId(ownerId: businessId)
.execute();
return result.data.teamHubs
final String businessId = await _queryRepository.getBusinessId();
final List<OrderHub> orderHubs =
await _queryRepository.getHubsByOwner(businessId);
return orderHubs
.map(
(dc.ListTeamHubsByOwnerIdTeamHubs hub) => RecurringOrderHubOption(
(OrderHub hub) => RecurringOrderHubOption(
id: hub.id,
name: hub.hubName,
name: hub.name,
address: hub.address,
placeId: hub.placeId,
latitude: hub.latitude,
@@ -213,6 +206,8 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
emit(state.copyWith(managers: event.managers));
}
/// Loads managers for the given [hubId] and maps them to presentation
/// option models.
Future<void> _loadManagersForHub(
String hubId,
Emitter<RecurringOrderState> emit,
@@ -220,22 +215,13 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
final List<RecurringOrderManagerOption>? managers =
await handleErrorWithResult(
action: () async {
final fdc.QueryResult<dc.ListTeamMembersData, void> result =
await _service.connector.listTeamMembers().execute();
return result.data.teamMembers
.where(
(dc.ListTeamMembersTeamMembers member) =>
member.teamHubId == hubId &&
member.role is dc.Known<dc.TeamMemberRole> &&
(member.role as dc.Known<dc.TeamMemberRole>).value ==
dc.TeamMemberRole.MANAGER,
)
final List<OrderManager> orderManagers =
await _queryRepository.getManagersByHub(hubId);
return orderManagers
.map(
(dc.ListTeamMembersTeamMembers member) =>
RecurringOrderManagerOption(
id: member.id,
name: member.user.fullName ?? 'Unknown',
(OrderManager m) => RecurringOrderManagerOption(
id: m.id,
name: m.name,
),
)
.toList();

View File

@@ -35,10 +35,9 @@ class HubManagerSelector extends StatelessWidget {
style: UiTypography.body1m.textPrimary,
),
if (description != null) ...<Widget>[
const SizedBox(height: UiConstants.space2),
Text(description!, style: UiTypography.body2r.textSecondary),
],
const SizedBox(height: UiConstants.space2),
const SizedBox(height: UiConstants.space3),
InkWell(
onTap: () => _showSelector(context),
borderRadius: BorderRadius.circular(UiConstants.radiusBase),