feat: Implement UI for adding new shift manager during order creation
This commit is contained in:
@@ -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].
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
},
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user