From 2643037c0b7d56b61561e87bee21e6e9d75f89d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Salazar?= <73718835+joshrs23@users.noreply.github.com> Date: Fri, 23 Jan 2026 15:33:15 -0500 Subject: [PATCH] getting informatin of vendor and vendorroles --- .../flutter/generated_plugin_registrant.cc | 4 + .../linux/flutter/generated_plugins.cmake | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 2 + .../flutter/generated_plugin_registrant.cc | 3 + .../windows/flutter/generated_plugins.cmake | 1 + .../lib/src/create_order_module.dart | 7 +- .../client_create_order_repository_impl.dart | 174 +----------------- .../blocs/one_time_order_bloc.dart | 84 +++++---- .../blocs/one_time_order_state.dart | 21 +++ .../presentation/blocs/rapid_order_bloc.dart | 3 + .../one_time_order_position_card.dart | 57 +++--- .../one_time_order/one_time_order_view.dart | 2 +- .../widgets/rapid_order/rapid_order_view.dart | 9 +- 13 files changed, 133 insertions(+), 235 deletions(-) diff --git a/apps/mobile/apps/client/linux/flutter/generated_plugin_registrant.cc b/apps/mobile/apps/client/linux/flutter/generated_plugin_registrant.cc index e71a16d2..f6f23bfe 100644 --- a/apps/mobile/apps/client/linux/flutter/generated_plugin_registrant.cc +++ b/apps/mobile/apps/client/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,10 @@ #include "generated_plugin_registrant.h" +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); } diff --git a/apps/mobile/apps/client/linux/flutter/generated_plugins.cmake b/apps/mobile/apps/client/linux/flutter/generated_plugins.cmake index 2e1de87a..f16b4c34 100644 --- a/apps/mobile/apps/client/linux/flutter/generated_plugins.cmake +++ b/apps/mobile/apps/client/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/apps/mobile/apps/client/macos/Flutter/GeneratedPluginRegistrant.swift b/apps/mobile/apps/client/macos/Flutter/GeneratedPluginRegistrant.swift index 8bd29968..c4ba9dcf 100644 --- a/apps/mobile/apps/client/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/apps/mobile/apps/client/macos/Flutter/GeneratedPluginRegistrant.swift @@ -9,10 +9,12 @@ import firebase_app_check import firebase_auth import firebase_core import shared_preferences_foundation +import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTFirebaseAppCheckPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAppCheckPlugin")) FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin")) FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/apps/mobile/apps/client/windows/flutter/generated_plugin_registrant.cc b/apps/mobile/apps/client/windows/flutter/generated_plugin_registrant.cc index d141b74f..869eecae 100644 --- a/apps/mobile/apps/client/windows/flutter/generated_plugin_registrant.cc +++ b/apps/mobile/apps/client/windows/flutter/generated_plugin_registrant.cc @@ -8,10 +8,13 @@ #include #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { FirebaseAuthPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("FirebaseAuthPluginCApi")); FirebaseCorePluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/apps/mobile/apps/client/windows/flutter/generated_plugins.cmake b/apps/mobile/apps/client/windows/flutter/generated_plugins.cmake index 29944d5b..7ba8383b 100644 --- a/apps/mobile/apps/client/windows/flutter/generated_plugins.cmake +++ b/apps/mobile/apps/client/windows/flutter/generated_plugins.cmake @@ -5,6 +5,7 @@ list(APPEND FLUTTER_PLUGIN_LIST firebase_auth firebase_core + url_launcher_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST 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 3c4d096c..f539b1bf 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 @@ -43,7 +43,12 @@ class ClientCreateOrderModule extends Module { // BLoCs i.addSingleton(ClientCreateOrderBloc.new); i.add(RapidOrderBloc.new); - i.add(OneTimeOrderBloc.new); + i.add( + () => OneTimeOrderBloc( + i.get(), + ExampleConnector.instance, + ), + ); } @override 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 90b29a5e..1aec25fb 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,4 @@ 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'; @@ -56,176 +55,7 @@ class ClientCreateOrderRepositoryImpl @override 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; + // TO-DO: connect IA and return array with the information. + throw UnimplementedError('Rapid order IA is not connected yet.'); } } diff --git a/apps/mobile/packages/features/client/create_order/lib/src/presentation/blocs/one_time_order_bloc.dart b/apps/mobile/packages/features/client/create_order/lib/src/presentation/blocs/one_time_order_bloc.dart index a4a0d193..ab4369f7 100644 --- a/apps/mobile/packages/features/client/create_order/lib/src/presentation/blocs/one_time_order_bloc.dart +++ b/apps/mobile/packages/features/client/create_order/lib/src/presentation/blocs/one_time_order_bloc.dart @@ -1,4 +1,5 @@ import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:krow_data_connect/krow_data_connect.dart' as dc; import 'package:krow_domain/krow_domain.dart'; import '../../domain/arguments/one_time_order_arguments.dart'; import '../../domain/usecases/create_one_time_order_usecase.dart'; @@ -7,7 +8,7 @@ import 'one_time_order_state.dart'; /// BLoC for managing the multi-step one-time order creation form. class OneTimeOrderBloc extends Bloc { - OneTimeOrderBloc(this._createOneTimeOrderUseCase) + OneTimeOrderBloc(this._createOneTimeOrderUseCase, this._dataConnect) : super(OneTimeOrderState.initial()) { on(_onVendorsLoaded); on(_onVendorChanged); @@ -18,52 +19,64 @@ class OneTimeOrderBloc extends Bloc { on(_onPositionUpdated); on(_onSubmitted); - // Initial load of mock vendors - add( - const OneTimeOrderVendorsLoaded([ - Vendor( - id: 'v1', - name: 'Elite Staffing', - rates: { - 'Server': 25.0, - 'Bartender': 30.0, - 'Cook': 28.0, - 'Busser': 18.0, - 'Host': 20.0, - 'Barista': 22.0, - 'Dishwasher': 17.0, - 'Event Staff': 19.0, - }, - ), - Vendor( - id: 'v2', - name: 'Premier Workforce', - rates: { - 'Server': 22.0, - 'Bartender': 28.0, - 'Cook': 25.0, - 'Busser': 16.0, - 'Host': 18.0, - 'Barista': 20.0, - 'Dishwasher': 15.0, - 'Event Staff': 18.0, - }, - ), - ]), - ); + _loadVendors(); } final CreateOneTimeOrderUseCase _createOneTimeOrderUseCase; + final dc.ExampleConnector _dataConnect; + + Future _loadVendors() async { + try { + final result = await _dataConnect.listVendors().execute(); + final vendors = result.data.vendors + .map( + (vendor) => Vendor( + id: vendor.id, + name: vendor.companyName, + rates: const {}, + ), + ) + .toList(); + add(OneTimeOrderVendorsLoaded(vendors)); + } catch (_) { + add(const OneTimeOrderVendorsLoaded([])); + } + } + + Future _loadRolesForVendor(String vendorId) async { + try { + final result = await _dataConnect.listRolesByVendorId( + vendorId: vendorId, + ).execute(); + final roles = result.data.roles + .map( + (role) => OneTimeOrderRoleOption( + id: role.id, + name: role.name, + costPerHour: role.costPerHour, + ), + ) + .toList(); + emit(state.copyWith(roles: roles)); + } catch (_) { + emit(state.copyWith(roles: const [])); + } + } void _onVendorsLoaded( OneTimeOrderVendorsLoaded event, Emitter emit, ) { + final Vendor? selectedVendor = + event.vendors.isNotEmpty ? event.vendors.first : null; emit( state.copyWith( vendors: event.vendors, - selectedVendor: event.vendors.isNotEmpty ? event.vendors.first : null, + selectedVendor: selectedVendor, ), ); + if (selectedVendor != null) { + _loadRolesForVendor(selectedVendor.id); + } } void _onVendorChanged( @@ -71,6 +84,7 @@ class OneTimeOrderBloc extends Bloc { Emitter emit, ) { emit(state.copyWith(selectedVendor: event.vendor)); + _loadRolesForVendor(event.vendor.id); } void _onDateChanged( diff --git a/apps/mobile/packages/features/client/create_order/lib/src/presentation/blocs/one_time_order_state.dart b/apps/mobile/packages/features/client/create_order/lib/src/presentation/blocs/one_time_order_state.dart index 03aee2fa..a6d7a06d 100644 --- a/apps/mobile/packages/features/client/create_order/lib/src/presentation/blocs/one_time_order_state.dart +++ b/apps/mobile/packages/features/client/create_order/lib/src/presentation/blocs/one_time_order_state.dart @@ -12,6 +12,7 @@ class OneTimeOrderState extends Equatable { this.errorMessage, this.vendors = const [], this.selectedVendor, + this.roles = const [], }); factory OneTimeOrderState.initial() { @@ -22,6 +23,7 @@ class OneTimeOrderState extends Equatable { OneTimeOrderPosition(role: '', count: 1, startTime: '', endTime: ''), ], vendors: const [], + roles: const [], ); } final DateTime date; @@ -31,6 +33,7 @@ class OneTimeOrderState extends Equatable { final String? errorMessage; final List vendors; final Vendor? selectedVendor; + final List roles; OneTimeOrderState copyWith({ DateTime? date, @@ -40,6 +43,7 @@ class OneTimeOrderState extends Equatable { String? errorMessage, List? vendors, Vendor? selectedVendor, + List? roles, }) { return OneTimeOrderState( date: date ?? this.date, @@ -49,6 +53,7 @@ class OneTimeOrderState extends Equatable { errorMessage: errorMessage ?? this.errorMessage, vendors: vendors ?? this.vendors, selectedVendor: selectedVendor ?? this.selectedVendor, + roles: roles ?? this.roles, ); } @@ -61,5 +66,21 @@ class OneTimeOrderState extends Equatable { errorMessage, vendors, selectedVendor, + roles, ]; } + +class OneTimeOrderRoleOption extends Equatable { + const OneTimeOrderRoleOption({ + required this.id, + required this.name, + required this.costPerHour, + }); + + final String id; + final String name; + final double costPerHour; + + @override + List get props => [id, name, costPerHour]; +} diff --git a/apps/mobile/packages/features/client/create_order/lib/src/presentation/blocs/rapid_order_bloc.dart b/apps/mobile/packages/features/client/create_order/lib/src/presentation/blocs/rapid_order_bloc.dart index f3b3b63b..4f3e6874 100644 --- a/apps/mobile/packages/features/client/create_order/lib/src/presentation/blocs/rapid_order_bloc.dart +++ b/apps/mobile/packages/features/client/create_order/lib/src/presentation/blocs/rapid_order_bloc.dart @@ -64,14 +64,17 @@ class RapidOrderBloc extends Bloc { final RapidOrderState currentState = state; if (currentState is RapidOrderInitial) { final String message = currentState.message; + print('RapidOrder submit: message="$message"'); emit(const RapidOrderSubmitting()); try { await _createRapidOrderUseCase( RapidOrderArguments(description: message), ); + print('RapidOrder submit: success'); emit(const RapidOrderSuccess()); } catch (e) { + print('RapidOrder submit: error=$e'); emit(RapidOrderFailure(e.toString())); } } diff --git a/apps/mobile/packages/features/client/create_order/lib/src/presentation/widgets/one_time_order/one_time_order_position_card.dart b/apps/mobile/packages/features/client/create_order/lib/src/presentation/widgets/one_time_order/one_time_order_position_card.dart index 4af5d168..0ea74c31 100644 --- a/apps/mobile/packages/features/client/create_order/lib/src/presentation/widgets/one_time_order/one_time_order_position_card.dart +++ b/apps/mobile/packages/features/client/create_order/lib/src/presentation/widgets/one_time_order/one_time_order_position_card.dart @@ -2,6 +2,7 @@ import 'package:core_localization/core_localization.dart'; import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:krow_domain/krow_domain.dart'; +import '../../blocs/one_time_order_state.dart'; /// A card widget for editing a specific position in a one-time order. /// Matches the prototype layout while using design system tokens. @@ -19,7 +20,7 @@ class OneTimeOrderPositionCard extends StatelessWidget { required this.startLabel, required this.endLabel, required this.lunchLabel, - this.vendor, + required this.roles, super.key, }); @@ -56,8 +57,8 @@ class OneTimeOrderPositionCard extends StatelessWidget { /// Label for the lunch break. final String lunchLabel; - /// The current selected vendor to determine rates. - final Vendor? vendor; + /// Available roles for the selected vendor. + final List roles; @override Widget build(BuildContext context) { @@ -118,26 +119,7 @@ class OneTimeOrderPositionCard extends StatelessWidget { onUpdated(position.copyWith(role: val)); } }, - items: - { - ...(vendor?.rates.keys ?? []), - if (position.role.isNotEmpty && - !(vendor?.rates.keys.contains(position.role) ?? - false)) - position.role, - }.map((String role) { - final double? rate = vendor?.rates[role]; - final String label = rate == null - ? role - : '$role - \$${rate.toStringAsFixed(0)}/hr'; - return DropdownMenuItem( - value: role, - child: Text( - label, - style: UiTypography.body2r.textPrimary, - ), - ); - }).toList(), + items: _buildRoleItems(), ), ), ), @@ -317,4 +299,33 @@ class OneTimeOrderPositionCard extends StatelessWidget { ], ); } + + List> _buildRoleItems() { + final items = roles + .map( + (role) => DropdownMenuItem( + value: role.id, + child: Text( + '${role.name} - \$${role.costPerHour.toStringAsFixed(0)}', + style: UiTypography.body2r.textPrimary, + ), + ), + ) + .toList(); + + final hasSelected = roles.any((role) => role.id == position.role); + if (position.role.isNotEmpty && !hasSelected) { + items.add( + DropdownMenuItem( + value: position.role, + child: Text( + position.role, + style: UiTypography.body2r.textPrimary, + ), + ), + ); + } + + return items; + } } diff --git a/apps/mobile/packages/features/client/create_order/lib/src/presentation/widgets/one_time_order/one_time_order_view.dart b/apps/mobile/packages/features/client/create_order/lib/src/presentation/widgets/one_time_order/one_time_order_view.dart index 19a27567..f2fba146 100644 --- a/apps/mobile/packages/features/client/create_order/lib/src/presentation/widgets/one_time_order/one_time_order_view.dart +++ b/apps/mobile/packages/features/client/create_order/lib/src/presentation/widgets/one_time_order/one_time_order_view.dart @@ -215,7 +215,7 @@ class _OneTimeOrderForm extends StatelessWidget { startLabel: labels.start_label, endLabel: labels.end_label, lunchLabel: labels.lunch_break_label, - vendor: state.selectedVendor, + roles: state.roles, onUpdated: (OneTimeOrderPosition updated) { BlocProvider.of( context, diff --git a/apps/mobile/packages/features/client/create_order/lib/src/presentation/widgets/rapid_order/rapid_order_view.dart b/apps/mobile/packages/features/client/create_order/lib/src/presentation/widgets/rapid_order/rapid_order_view.dart index 1f758d89..093ec39d 100644 --- a/apps/mobile/packages/features/client/create_order/lib/src/presentation/widgets/rapid_order/rapid_order_view.dart +++ b/apps/mobile/packages/features/client/create_order/lib/src/presentation/widgets/rapid_order/rapid_order_view.dart @@ -288,9 +288,12 @@ class _RapidOrderActions extends StatelessWidget { trailingIcon: UiIcons.arrowRight, onPressed: isSubmitting || isMessageEmpty ? null - : () => BlocProvider.of( - context, - ).add(const RapidOrderSubmitted()), + : () { + print('RapidOrder send pressed'); + BlocProvider.of( + context, + ).add(const RapidOrderSubmitted()); + }, ), ), ],