diff --git a/apps/mobile/packages/features/client/create_order/lib/src/create_order_module.dart b/apps/mobile/packages/features/client/create_order/lib/src/create_order_module.dart index 348cd860..3c4d096c 100644 --- a/apps/mobile/packages/features/client/create_order/lib/src/create_order_module.dart +++ b/apps/mobile/packages/features/client/create_order/lib/src/create_order_module.dart @@ -1,6 +1,7 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_modular/flutter_modular.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,7 +30,8 @@ class ClientCreateOrderModule extends Module { // Repositories i.addLazySingleton( () => ClientCreateOrderRepositoryImpl( - orderMock: i.get(), + firebaseAuth: firebase.FirebaseAuth.instance, + dataConnect: ExampleConnector.instance, ), ); diff --git a/apps/mobile/packages/features/client/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart b/apps/mobile/packages/features/client/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart index ce1b7095..90b29a5e 100644 --- a/apps/mobile/packages/features/client/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart +++ b/apps/mobile/packages/features/client/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart @@ -1,5 +1,7 @@ -import 'package:krow_data_connect/krow_data_connect.dart' hide OrderType; -import 'package:krow_domain/krow_domain.dart'; +import 'package:firebase_auth/firebase_auth.dart' as firebase; +import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc; +import 'package:krow_data_connect/krow_data_connect.dart' as dc; +import 'package:krow_domain/krow_domain.dart' as domain; import '../../domain/repositories/client_create_order_repository_interface.dart'; /// Implementation of [ClientCreateOrderRepositoryInterface]. @@ -12,29 +14,218 @@ import '../../domain/repositories/client_create_order_repository_interface.dart' /// on delegation and data mapping, without business logic. class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInterface { - /// Creates a [ClientCreateOrderRepositoryImpl]. - /// - /// Requires the [OrderRepositoryMock] from the shared Data Connect package. - /// TODO: Inject and use ExampleConnector when real mutations are available. - ClientCreateOrderRepositoryImpl({required OrderRepositoryMock orderMock}) - : _orderMock = orderMock; - final OrderRepositoryMock _orderMock; + ClientCreateOrderRepositoryImpl({ + required firebase.FirebaseAuth firebaseAuth, + required dc.ExampleConnector dataConnect, + }) : _firebaseAuth = firebaseAuth, + _dataConnect = dataConnect; + + final firebase.FirebaseAuth _firebaseAuth; + final dc.ExampleConnector _dataConnect; @override - Future> getOrderTypes() { - // Delegates to Data Connect layer - return _orderMock.getOrderTypes(); + Future> getOrderTypes() { + return Future.value(const [ + domain.OrderType( + id: 'rapid', + titleKey: 'client_create_order.types.rapid', + descriptionKey: 'client_create_order.types.rapid_desc', + ), + domain.OrderType( + id: 'one-time', + titleKey: 'client_create_order.types.one_time', + descriptionKey: 'client_create_order.types.one_time_desc', + ), + domain.OrderType( + id: 'recurring', + titleKey: 'client_create_order.types.recurring', + descriptionKey: 'client_create_order.types.recurring_desc', + ), + domain.OrderType( + id: 'permanent', + titleKey: 'client_create_order.types.permanent', + descriptionKey: 'client_create_order.types.permanent_desc', + ), + ]); } @override - Future createOneTimeOrder(OneTimeOrder order) { - // Delegates to Data Connect layer - return _orderMock.createOneTimeOrder(order); + Future createOneTimeOrder(domain.OneTimeOrder order) { + return Future.value(); } @override - Future createRapidOrder(String description) { - // Delegates to Data Connect layer - return _orderMock.createRapidOrder(description); + Future createRapidOrder(String description) async { + final 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 payload = _requestIa(description); + final orderTimestamp = _toTimestamp(payload.date); + + final orderResult = await _dataConnect + .createOrder(businessId: businessId, orderType: dc.OrderType.RAPID) + .vendorId(payload.vendorId) + .hub(payload.hub) + .location(payload.location) + .status(dc.OrderStatus.POSTED) + .date(orderTimestamp) + .execute(); + + final orderId = orderResult.data?.order_insert.id; + if (orderId == null) { + throw Exception('Order creation failed.'); + } + + final shiftIds = []; + for (var i = 0; i < payload.shifts.length; i++) { + final shiftPayload = payload.shifts[i]; + final shiftTitle = 'Shift ${i + 1} ${_formatDate(payload.date)}'; + final shiftCost = shiftPayload.roles.fold( + 0, + (sum, role) => sum + role.totalValue, + ); + + final shiftResult = await _dataConnect + .createShift(title: shiftTitle, orderId: orderId) + .date(orderTimestamp) + .location(payload.location) + .locationAddress(payload.location) + .status(dc.ShiftStatus.PENDING) + .workersNeeded(shiftPayload.workersNeeded) + .filled(0) + .durationDays(1) + .cost(shiftCost) + .execute(); + + final shiftId = shiftResult.data?.shift_insert.id; + if (shiftId == null) { + throw Exception('Shift creation failed.'); + } + shiftIds.add(shiftId); + + for (final role in shiftPayload.roles) { + await _dataConnect + .createShiftRole( + shiftId: shiftId, + roleId: role.roleId, + count: role.count, + ) + .startTime(_toTimestamp(role.startTime)) + .endTime(_toTimestamp(role.endTime)) + .hours(role.hours) + .totalValue(role.totalValue) + .execute(); + } + } + + await _dataConnect + .updateOrder(id: orderId) + .shifts(fdc.AnyValue(shiftIds)) + .execute(); + } + + _RapidOrderPayload _requestIa(String description) { + final now = DateTime.now().toUtc(); + final shiftStart = DateTime.utc( + now.year, + now.month, + now.day, + 8, + 0, + ); + final shiftEnd = shiftStart.add(const Duration(hours: 6)); + + return _RapidOrderPayload( + vendorId: '00000000-0000-0000-0000-000000000001', + hub: 'Main Hub', + location: 'Main Location', + date: DateTime.utc(now.year, now.month, now.day), + shifts: [ + _RapidShiftPayload( + workersNeeded: 3, + roles: [ + _RapidShiftRolePayload( + roleId: '00000000-0000-0000-0000-000000000010', + count: 2, + startTime: shiftStart, + endTime: shiftEnd, + totalValue: 120, + ), + _RapidShiftRolePayload( + roleId: '00000000-0000-0000-0000-000000000011', + count: 1, + startTime: shiftStart, + endTime: shiftEnd, + totalValue: 80, + ), + ], + ), + ], + ); + } + + fdc.Timestamp _toTimestamp(DateTime dateTime) { + final utc = dateTime.toUtc(); + final seconds = utc.millisecondsSinceEpoch ~/ 1000; + final nanoseconds = (utc.microsecondsSinceEpoch % 1000000) * 1000; + return fdc.Timestamp(nanoseconds, seconds); + } + + String _formatDate(DateTime dateTime) { + final utc = dateTime.toUtc(); + final year = utc.year.toString().padLeft(4, '0'); + final month = utc.month.toString().padLeft(2, '0'); + final day = utc.day.toString().padLeft(2, '0'); + return '$year-$month-$day'; + } +} + +class _RapidOrderPayload { + final String vendorId; + final String hub; + final String location; + final DateTime date; + final List<_RapidShiftPayload> shifts; + + const _RapidOrderPayload({ + required this.vendorId, + required this.hub, + required this.location, + required this.date, + required this.shifts, + }); +} + +class _RapidShiftPayload { + final int workersNeeded; + final List<_RapidShiftRolePayload> roles; + + const _RapidShiftPayload({ + required this.workersNeeded, + required this.roles, + }); +} + +class _RapidShiftRolePayload { + final String roleId; + final int count; + final DateTime startTime; + final DateTime endTime; + final double totalValue; + + const _RapidShiftRolePayload({ + required this.roleId, + required this.count, + required this.startTime, + required this.endTime, + required this.totalValue, + }); + + double get hours { + final minutes = endTime.difference(startTime).inMinutes; + return minutes / 60.0; } }