feat: Refactor ClientCreateOrderRepositoryImpl and OneTimeOrderBloc to utilize DataConnectService, removing FirebaseAuth dependency

This commit is contained in:
Achintha Isuru
2026-02-16 17:35:15 -05:00
parent fc0bb5828c
commit 19eda09620
3 changed files with 99 additions and 132 deletions

View File

@@ -2,7 +2,6 @@ import 'package:flutter/widgets.dart';
import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_core/core.dart';
import 'package:krow_data_connect/krow_data_connect.dart';
import 'package:firebase_auth/firebase_auth.dart' as firebase;
import 'data/repositories_impl/client_create_order_repository_impl.dart';
import 'domain/repositories/client_create_order_repository_interface.dart';
import 'domain/usecases/create_one_time_order_usecase.dart';
@@ -29,12 +28,7 @@ class ClientCreateOrderModule extends Module {
@override
void binds(Injector i) {
// Repositories
i.addLazySingleton<ClientCreateOrderRepositoryInterface>(
() => ClientCreateOrderRepositoryImpl(
firebaseAuth: firebase.FirebaseAuth.instance,
dataConnect: ExampleConnector.instance,
),
);
i.addLazySingleton<ClientCreateOrderRepositoryInterface>(ClientCreateOrderRepositoryImpl.new);
// UseCases
i.addLazySingleton(GetOrderTypesUseCase.new);
@@ -44,12 +38,7 @@ class ClientCreateOrderModule extends Module {
// BLoCs
i.add<ClientCreateOrderBloc>(ClientCreateOrderBloc.new);
i.add<RapidOrderBloc>(RapidOrderBloc.new);
i.add<OneTimeOrderBloc>(
() => OneTimeOrderBloc(
i.get<CreateOneTimeOrderUseCase>(),
ExampleConnector.instance,
),
);
i.add<OneTimeOrderBloc>(OneTimeOrderBloc.new);
}
@override

View File

@@ -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;
@@ -13,21 +12,16 @@ import '../../domain/repositories/client_create_order_repository_interface.dart'
///
/// It follows the KROW Clean Architecture by keeping the data layer focused
/// on delegation and data mapping, without business logic.
class ClientCreateOrderRepositoryImpl
with dc.DataErrorHandler
implements ClientCreateOrderRepositoryInterface {
class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInterface {
ClientCreateOrderRepositoryImpl({
required firebase.FirebaseAuth firebaseAuth,
required dc.ExampleConnector dataConnect,
}) : _firebaseAuth = firebaseAuth,
_dataConnect = dataConnect;
required dc.DataConnectService service,
}) : _service = service;
final firebase.FirebaseAuth _firebaseAuth;
final dc.ExampleConnector _dataConnect;
final dc.DataConnectService _service;
@override
Future<List<domain.OrderType>> getOrderTypes() {
return Future.value(const <domain.OrderType>[
return Future<List<domain.OrderType>>.value(const <domain.OrderType>[
domain.OrderType(
id: 'one-time',
titleKey: 'client_create_order.types.one_time',
@@ -55,100 +49,95 @@ class ClientCreateOrderRepositoryImpl
@override
Future<void> createOneTimeOrder(domain.OneTimeOrder order) 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 String? vendorId = order.vendorId;
if (vendorId == null || vendorId.isEmpty) {
throw Exception('Vendor is missing.');
}
final domain.OneTimeOrderHubDetails? hub = order.hub;
if (hub == null || hub.id.isEmpty) {
throw Exception('Hub is missing.');
}
return _service.run(() async {
final String businessId = await _service.getBusinessId();
final String? vendorId = order.vendorId;
if (vendorId == null || vendorId.isEmpty) {
throw Exception('Vendor is missing.');
}
final domain.OneTimeOrderHubDetails? hub = order.hub;
if (hub == null || hub.id.isEmpty) {
throw Exception('Hub is missing.');
}
final DateTime orderDateOnly = DateTime(
order.date.year,
order.date.month,
order.date.day,
);
final fdc.Timestamp orderTimestamp = _toTimestamp(orderDateOnly);
final fdc.OperationResult<dc.CreateOrderData, dc.CreateOrderVariables>
orderResult = await executeProtected(() => _dataConnect
.createOrder(
businessId: businessId,
orderType: dc.OrderType.ONE_TIME,
teamHubId: hub.id,
final DateTime orderDateOnly = DateTime(
order.date.year,
order.date.month,
order.date.day,
);
final fdc.Timestamp orderTimestamp = _service.toTimestamp(orderDateOnly);
final fdc.OperationResult<dc.CreateOrderData, dc.CreateOrderVariables> orderResult =
await _service.connector
.createOrder(
businessId: businessId,
orderType: dc.OrderType.ONE_TIME,
teamHubId: hub.id,
)
.vendorId(vendorId)
.eventName(order.eventName)
.status(dc.OrderStatus.POSTED)
.date(orderTimestamp)
.execute();
final String orderId = orderResult.data.order_insert.id;
final int workersNeeded = order.positions.fold<int>(
0,
(int sum, domain.OneTimeOrderPosition position) => sum + position.count,
);
final String shiftTitle = 'Shift 1 ${_formatDate(order.date)}';
final double shiftCost = _calculateShiftCost(order);
final fdc.OperationResult<dc.CreateShiftData, dc.CreateShiftVariables> shiftResult =
await _service.connector
.createShift(title: shiftTitle, orderId: orderId)
.date(orderTimestamp)
.location(hub.name)
.locationAddress(hub.address)
.latitude(hub.latitude)
.longitude(hub.longitude)
.placeId(hub.placeId)
.city(hub.city)
.state(hub.state)
.street(hub.street)
.country(hub.country)
.status(dc.ShiftStatus.PENDING)
.workersNeeded(workersNeeded)
.filled(0)
.durationDays(1)
.cost(shiftCost)
.execute();
final String shiftId = shiftResult.data.shift_insert.id;
for (final domain.OneTimeOrderPosition position in order.positions) {
final DateTime start = _parseTime(order.date, position.startTime);
final DateTime end = _parseTime(order.date, position.endTime);
final DateTime normalizedEnd = end.isBefore(start) ? end.add(const Duration(days: 1)) : end;
final double hours = normalizedEnd.difference(start).inMinutes / 60.0;
final double rate = order.roleRates[position.role] ?? 0;
final double totalValue = rate * hours * position.count;
await _service.connector
.createShiftRole(
shiftId: shiftId,
roleId: position.role,
count: position.count,
)
.vendorId(vendorId)
.eventName(order.eventName)
.status(dc.OrderStatus.POSTED)
.date(orderTimestamp)
.execute());
.startTime(_service.toTimestamp(start))
.endTime(_service.toTimestamp(normalizedEnd))
.hours(hours)
.breakType(_breakDurationFromValue(position.lunchBreak))
.isBreakPaid(_isBreakPaid(position.lunchBreak))
.totalValue(totalValue)
.execute();
}
final String orderId = orderResult.data.order_insert.id;
final int workersNeeded = order.positions.fold<int>(
0,
(int sum, domain.OneTimeOrderPosition position) => sum + position.count,
);
final String shiftTitle = 'Shift 1 ${_formatDate(order.date)}';
final double shiftCost = _calculateShiftCost(order);
final fdc.OperationResult<dc.CreateShiftData, dc.CreateShiftVariables>
shiftResult = await executeProtected(() => _dataConnect
.createShift(title: shiftTitle, orderId: orderId)
.date(orderTimestamp)
.location(hub.name)
.locationAddress(hub.address)
.latitude(hub.latitude)
.longitude(hub.longitude)
.placeId(hub.placeId)
.city(hub.city)
.state(hub.state)
.street(hub.street)
.country(hub.country)
.status(dc.ShiftStatus.PENDING)
.workersNeeded(workersNeeded)
.filled(0)
.durationDays(1)
.cost(shiftCost)
.execute());
final String shiftId = shiftResult.data.shift_insert.id;
for (final domain.OneTimeOrderPosition position in order.positions) {
final DateTime start = _parseTime(order.date, position.startTime);
final DateTime end = _parseTime(order.date, position.endTime);
final DateTime normalizedEnd =
end.isBefore(start) ? end.add(const Duration(days: 1)) : end;
final double hours = normalizedEnd.difference(start).inMinutes / 60.0;
final double rate = order.roleRates[position.role] ?? 0;
final double totalValue = rate * hours * position.count;
await executeProtected(() => _dataConnect
.createShiftRole(
shiftId: shiftId,
roleId: position.role,
count: position.count,
)
.startTime(_toTimestamp(start))
.endTime(_toTimestamp(normalizedEnd))
.hours(hours)
.breakType(_breakDurationFromValue(position.lunchBreak))
.isBreakPaid(_isBreakPaid(position.lunchBreak))
.totalValue(totalValue)
.execute());
}
await executeProtected(() => _dataConnect
.updateOrder(id: orderId, teamHubId: hub.id)
.shifts(fdc.AnyValue(<String>[shiftId]))
.execute());
await _service.connector
.updateOrder(id: orderId, teamHubId: hub.id)
.shifts(fdc.AnyValue(<String>[shiftId]))
.execute();
});
}
@override
@@ -213,13 +202,6 @@ class ClientCreateOrderRepositoryImpl
);
}
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);
}
String _formatDate(DateTime dateTime) {
final String year = dateTime.year.toString().padLeft(4, '0');
final String month = dateTime.month.toString().padLeft(2, '0');

View File

@@ -11,7 +11,7 @@ import 'one_time_order_state.dart';
/// BLoC for managing the multi-step one-time order creation form.
class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
with BlocErrorHandler<OneTimeOrderState>, SafeBloc<OneTimeOrderEvent, OneTimeOrderState> {
OneTimeOrderBloc(this._createOneTimeOrderUseCase, this._dataConnect)
OneTimeOrderBloc(this._createOneTimeOrderUseCase, this._service)
: super(OneTimeOrderState.initial()) {
on<OneTimeOrderVendorsLoaded>(_onVendorsLoaded);
on<OneTimeOrderVendorChanged>(_onVendorChanged);
@@ -28,13 +28,13 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
_loadHubs();
}
final CreateOneTimeOrderUseCase _createOneTimeOrderUseCase;
final dc.ExampleConnector _dataConnect;
final dc.DataConnectService _service;
Future<void> _loadVendors() async {
final List<Vendor>? vendors = await handleErrorWithResult(
action: () async {
final QueryResult<dc.ListVendorsData, void> result =
await _dataConnect.listVendors().execute();
await _service.connector.listVendors().execute();
return result.data.vendors
.map(
(dc.ListVendorsVendors vendor) => Vendor(
@@ -57,7 +57,7 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
final List<OneTimeOrderRoleOption>? roles = await handleErrorWithResult(
action: () async {
final QueryResult<dc.ListRolesByVendorIdData, dc.ListRolesByVendorIdVariables>
result = await _dataConnect.listRolesByVendorId(vendorId: vendorId).execute();
result = await _service.connector.listRolesByVendorId(vendorId: vendorId).execute();
return result.data.roles
.map(
(dc.ListRolesByVendorIdRoles role) => OneTimeOrderRoleOption(
@@ -79,13 +79,9 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
Future<void> _loadHubs() async {
final List<OneTimeOrderHubOption>? hubs = await handleErrorWithResult(
action: () async {
final String? businessId =
dc.ClientSessionStore.instance.session?.business?.id;
if (businessId == null || businessId.isEmpty) {
return <OneTimeOrderHubOption>[];
}
final String businessId = await _service.getBusinessId();
final QueryResult<dc.ListTeamHubsByOwnerIdData, dc.ListTeamHubsByOwnerIdVariables>
result = await _dataConnect
result = await _service.connector
.listTeamHubsByOwnerId(ownerId: businessId)
.execute();
return result.data.teamHubs