From 9d25fd44ccd49e0307ec0f2ca0d05c0a68487856 Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Fri, 27 Feb 2026 13:16:11 -0500 Subject: [PATCH 1/2] Add rapid order parsing & audio recording Add support for transcribing and parsing rapid (urgent) orders into one-time order drafts. Introduces ParseRapidOrderTextToOrderUseCase and wiring for TranscribeRapidOrderUseCase, implements parseRapidOrder and transcribeRapidOrder in the client repository, and injects these into the RapidOrderBloc and module. Adds the record package dependency and registers the record plugin for iOS/macOS targets. Updates OneTimeOrder state, bloc and views to handle rapid-order drafts and navigate to the one-time order flow after parsing. Also includes small formatting and navigator changes. --- .../ios/Runner/GeneratedPluginRegistrant.m | 7 ++ .../Flutter/GeneratedPluginRegistrant.swift | 2 + .../ios/Runner/GeneratedPluginRegistrant.m | 7 ++ .../Flutter/GeneratedPluginRegistrant.swift | 2 + .../core/lib/src/routing/staff/navigator.dart | 4 +- apps/mobile/packages/core/pubspec.yaml | 1 + .../lib/src/create_order_module.dart | 12 +++- .../client_create_order_repository_impl.dart | 45 +++++++++++++ ...ent_create_order_repository_interface.dart | 10 +++ .../usecases/parse_rapid_order_usecase.dart | 15 +++++ .../one_time_order/one_time_order_bloc.dart | 50 +++++++++++++-- .../one_time_order/one_time_order_state.dart | 11 ++-- .../blocs/rapid_order/rapid_order_bloc.dart | 22 ++++--- .../blocs/rapid_order/rapid_order_state.dart | 9 +++ .../pages/one_time_order_page.dart | 1 + .../widgets/rapid_order/rapid_order_view.dart | 7 ++ .../one_time_order/one_time_order_view.dart | 17 ++--- .../Flutter/GeneratedPluginRegistrant.swift | 2 + apps/mobile/pubspec.lock | 64 +++++++++++++++++++ 19 files changed, 256 insertions(+), 32 deletions(-) create mode 100644 apps/mobile/packages/features/client/orders/create_order/lib/src/domain/usecases/parse_rapid_order_usecase.dart diff --git a/apps/mobile/apps/client/ios/Runner/GeneratedPluginRegistrant.m b/apps/mobile/apps/client/ios/Runner/GeneratedPluginRegistrant.m index 8b0a7da5..241fcf3b 100644 --- a/apps/mobile/apps/client/ios/Runner/GeneratedPluginRegistrant.m +++ b/apps/mobile/apps/client/ios/Runner/GeneratedPluginRegistrant.m @@ -36,6 +36,12 @@ @import image_picker_ios; #endif +#if __has_include() +#import +#else +@import record_ios; +#endif + #if __has_include() #import #else @@ -56,6 +62,7 @@ [FLTFirebaseAuthPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseAuthPlugin"]]; [FLTFirebaseCorePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseCorePlugin"]]; [FLTImagePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTImagePickerPlugin"]]; + [RecordIosPlugin registerWithRegistrar:[registry registrarForPlugin:@"RecordIosPlugin"]]; [SharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"SharedPreferencesPlugin"]]; [URLLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"URLLauncherPlugin"]]; } diff --git a/apps/mobile/apps/client/macos/Flutter/GeneratedPluginRegistrant.swift b/apps/mobile/apps/client/macos/Flutter/GeneratedPluginRegistrant.swift index 30780dc6..1dea22d7 100644 --- a/apps/mobile/apps/client/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/apps/mobile/apps/client/macos/Flutter/GeneratedPluginRegistrant.swift @@ -10,6 +10,7 @@ import file_selector_macos import firebase_app_check import firebase_auth import firebase_core +import record_macos import shared_preferences_foundation import url_launcher_macos @@ -19,6 +20,7 @@ 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")) + RecordMacOsPlugin.register(with: registry.registrar(forPlugin: "RecordMacOsPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/apps/mobile/apps/staff/ios/Runner/GeneratedPluginRegistrant.m b/apps/mobile/apps/staff/ios/Runner/GeneratedPluginRegistrant.m index e8a688bb..72d03754 100644 --- a/apps/mobile/apps/staff/ios/Runner/GeneratedPluginRegistrant.m +++ b/apps/mobile/apps/staff/ios/Runner/GeneratedPluginRegistrant.m @@ -54,6 +54,12 @@ @import permission_handler_apple; #endif +#if __has_include() +#import +#else +@import record_ios; +#endif + #if __has_include() #import #else @@ -77,6 +83,7 @@ [FLTGoogleMapsPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTGoogleMapsPlugin"]]; [FLTImagePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTImagePickerPlugin"]]; [PermissionHandlerPlugin registerWithRegistrar:[registry registrarForPlugin:@"PermissionHandlerPlugin"]]; + [RecordIosPlugin registerWithRegistrar:[registry registrarForPlugin:@"RecordIosPlugin"]]; [SharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"SharedPreferencesPlugin"]]; [URLLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"URLLauncherPlugin"]]; } diff --git a/apps/mobile/apps/staff/macos/Flutter/GeneratedPluginRegistrant.swift b/apps/mobile/apps/staff/macos/Flutter/GeneratedPluginRegistrant.swift index 56b4b1e5..e919f640 100644 --- a/apps/mobile/apps/staff/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/apps/mobile/apps/staff/macos/Flutter/GeneratedPluginRegistrant.swift @@ -11,6 +11,7 @@ import firebase_app_check import firebase_auth import firebase_core import geolocator_apple +import record_macos import shared_preferences_foundation import url_launcher_macos @@ -21,6 +22,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin")) FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) + RecordMacOsPlugin.register(with: registry.registrar(forPlugin: "RecordMacOsPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/apps/mobile/packages/core/lib/src/routing/staff/navigator.dart b/apps/mobile/packages/core/lib/src/routing/staff/navigator.dart index 5456d1e6..21d5c9ca 100644 --- a/apps/mobile/packages/core/lib/src/routing/staff/navigator.dart +++ b/apps/mobile/packages/core/lib/src/routing/staff/navigator.dart @@ -139,7 +139,7 @@ extension StaffNavigator on IModularNavigator { /// /// Manage personal information, documents, and preferences. void toProfile() { - pushNamedAndRemoveUntil(StaffPaths.profile, (_) => false); + navigate(StaffPaths.profile); } // ========================================================================== @@ -189,7 +189,7 @@ extension StaffNavigator on IModularNavigator { /// /// Record previous work experience and qualifications. void toExperience() { - pushNamed(StaffPaths.experience); + navigate(StaffPaths.experience); } /// Pushes the attire preferences page. diff --git a/apps/mobile/packages/core/pubspec.yaml b/apps/mobile/packages/core/pubspec.yaml index 08ec902f..15f91f58 100644 --- a/apps/mobile/packages/core/pubspec.yaml +++ b/apps/mobile/packages/core/pubspec.yaml @@ -25,4 +25,5 @@ dependencies: image_picker: ^1.1.2 path_provider: ^2.1.3 file_picker: ^8.1.7 + record: ^6.2.0 firebase_auth: ^6.1.4 diff --git a/apps/mobile/packages/features/client/orders/create_order/lib/src/create_order_module.dart b/apps/mobile/packages/features/client/orders/create_order/lib/src/create_order_module.dart index e459dd35..d5a51815 100644 --- a/apps/mobile/packages/features/client/orders/create_order/lib/src/create_order_module.dart +++ b/apps/mobile/packages/features/client/orders/create_order/lib/src/create_order_module.dart @@ -9,6 +9,8 @@ import 'domain/usecases/create_permanent_order_usecase.dart'; import 'domain/usecases/create_recurring_order_usecase.dart'; import 'domain/usecases/create_rapid_order_usecase.dart'; import 'domain/usecases/get_order_details_for_reorder_usecase.dart'; +import 'domain/usecases/parse_rapid_order_usecase.dart'; +import 'domain/usecases/transcribe_rapid_order_usecase.dart'; import 'presentation/blocs/index.dart'; import 'presentation/pages/create_order_page.dart'; import 'presentation/pages/one_time_order_page.dart'; @@ -37,10 +39,18 @@ class ClientCreateOrderModule extends Module { i.addLazySingleton(CreatePermanentOrderUseCase.new); i.addLazySingleton(CreateRecurringOrderUseCase.new); i.addLazySingleton(CreateRapidOrderUseCase.new); + i.addLazySingleton(TranscribeRapidOrderUseCase.new); + i.addLazySingleton(ParseRapidOrderTextToOrderUseCase.new); i.addLazySingleton(GetOrderDetailsForReorderUseCase.new); // BLoCs - i.add(RapidOrderBloc.new); + i.add( + (Injector i) => RapidOrderBloc( + i.get(), + i.get(), + i.get(), + ), + ); i.add(OneTimeOrderBloc.new); i.add(PermanentOrderBloc.new); i.add(RecurringOrderBloc.new); diff --git a/apps/mobile/packages/features/client/orders/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart b/apps/mobile/packages/features/client/orders/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart index aea8a443..645ce1df 100644 --- a/apps/mobile/packages/features/client/orders/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart +++ b/apps/mobile/packages/features/client/orders/create_order/lib/src/data/repositories_impl/client_create_order_repository_impl.dart @@ -367,6 +367,51 @@ class ClientCreateOrderRepositoryImpl throw UnimplementedError('Rapid order IA is not connected yet.'); } + @override + Future parseRapidOrder(String text) async { + final RapidOrderParseResponse response = await _rapidOrderService.parseText( + text: text, + ); + final RapidOrderParsedData data = response.parsed; + + final DateTime startAt = + DateTime.tryParse(data.startAt ?? '') ?? DateTime.now(); + final DateTime endAt = + DateTime.tryParse(data.endAt ?? '') ?? + startAt.add(const Duration(hours: 8)); + + final String startTimeStr = DateFormat('hh:mm a').format(startAt); + final String endTimeStr = DateFormat('hh:mm a').format(endAt); + + return domain.OneTimeOrder( + date: startAt, + location: data.locationHint ?? '', + eventName: data.notes ?? '', + hub: data.locationHint != null + ? domain.OneTimeOrderHubDetails( + id: '', + name: data.locationHint!, + address: '', + ) + : null, + positions: data.positions.map((RapidOrderPosition p) { + return domain.OneTimeOrderPosition( + role: p.role, + count: p.count, + startTime: startTimeStr, + endTime: endTimeStr, + ); + }).toList(), + ); + } + + @override + Future transcribeRapidOrder(String audioPath) async { + final RapidOrderTranscriptionResponse response = await _rapidOrderService + .transcribeAudio(audioFileUri: audioPath); + return response.transcript; + } + @override Future reorder(String previousOrderId, DateTime newDate) async { // TODO: Implement reorder functionality to fetch the previous order and create a new one with the updated date. diff --git a/apps/mobile/packages/features/client/orders/create_order/lib/src/domain/repositories/client_create_order_repository_interface.dart b/apps/mobile/packages/features/client/orders/create_order/lib/src/domain/repositories/client_create_order_repository_interface.dart index a2c80cd5..84124804 100644 --- a/apps/mobile/packages/features/client/orders/create_order/lib/src/domain/repositories/client_create_order_repository_interface.dart +++ b/apps/mobile/packages/features/client/orders/create_order/lib/src/domain/repositories/client_create_order_repository_interface.dart @@ -24,6 +24,16 @@ abstract interface class ClientCreateOrderRepositoryInterface { /// [description] is the text message (or transcribed voice) describing the need. Future createRapidOrder(String description); + /// Transcribes the audio file for a rapid order. + /// + /// [audioPath] is the local path to the recorded audio file. + Future transcribeRapidOrder(String audioPath); + + /// Parses the text description for a rapid order into a structured draft. + /// + /// [text] is the text message describing the need. + Future parseRapidOrder(String text); + /// Reorders an existing staffing order with a new date. /// /// [previousOrderId] is the ID of the order to reorder. diff --git a/apps/mobile/packages/features/client/orders/create_order/lib/src/domain/usecases/parse_rapid_order_usecase.dart b/apps/mobile/packages/features/client/orders/create_order/lib/src/domain/usecases/parse_rapid_order_usecase.dart new file mode 100644 index 00000000..17113b2a --- /dev/null +++ b/apps/mobile/packages/features/client/orders/create_order/lib/src/domain/usecases/parse_rapid_order_usecase.dart @@ -0,0 +1,15 @@ +import 'package:krow_domain/krow_domain.dart'; +import '../repositories/client_create_order_repository_interface.dart'; + +/// Use case for parsing rapid order text into a structured OneTimeOrder. +class ParseRapidOrderTextToOrderUseCase { + ParseRapidOrderTextToOrderUseCase({ + required ClientCreateOrderRepositoryInterface repository, + }) : _repository = repository; + + final ClientCreateOrderRepositoryInterface _repository; + + Future call(String text) async { + return _repository.parseRapidOrder(text); + } +} diff --git a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/blocs/one_time_order/one_time_order_bloc.dart b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/blocs/one_time_order/one_time_order_bloc.dart index a255fe7d..7c3a4435 100644 --- a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/blocs/one_time_order/one_time_order_bloc.dart +++ b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/blocs/one_time_order/one_time_order_bloc.dart @@ -136,9 +136,7 @@ class OneTimeOrderBloc extends Bloc } } - Future _loadManagersForHub( - String hubId, - ) async { + Future _loadManagersForHub(String hubId) async { final List? managers = await handleErrorWithResult( action: () async { @@ -163,7 +161,9 @@ class OneTimeOrderBloc extends Bloc .toList(); }, onError: (_) { - add(const OneTimeOrderManagersLoaded([])); + add( + const OneTimeOrderManagersLoaded([]), + ); }, ); @@ -172,7 +172,6 @@ class OneTimeOrderBloc extends Bloc } } - Future _onVendorsLoaded( OneTimeOrderVendorsLoaded event, Emitter emit, @@ -216,7 +215,6 @@ class OneTimeOrderBloc extends Bloc } } - void _onHubChanged( OneTimeOrderHubChanged event, Emitter emit, @@ -239,7 +237,6 @@ class OneTimeOrderBloc extends Bloc emit(state.copyWith(managers: event.managers)); } - void _onEventNameChanged( OneTimeOrderEventNameChanged event, Emitter emit, @@ -349,6 +346,45 @@ class OneTimeOrderBloc extends Bloc final DateTime? startDate = data['startDate'] as DateTime?; final String? orderId = data['orderId']?.toString(); + // Handle Rapid Order Draft + if (data['isRapidDraft'] == true) { + final OneTimeOrder? order = data['order'] as OneTimeOrder?; + if (order != null) { + emit( + state.copyWith( + eventName: order.eventName ?? '', + date: order.date, + positions: order.positions, + location: order.location, + isRapidDraft: true, + ), + ); + + // Try to match vendor if available + if (order.vendorId != null) { + final Vendor? vendor = state.vendors + .where((Vendor v) => v.id == order.vendorId) + .firstOrNull; + if (vendor != null) { + emit(state.copyWith(selectedVendor: vendor)); + await _loadRolesForVendor(vendor.id, emit); + } + } + + // Try to match hub if available + if (order.hub != null) { + final OneTimeOrderHubOption? hub = state.hubs + .where((OneTimeOrderHubOption h) => h.id == order.hub?.id) + .firstOrNull; + if (hub != null) { + emit(state.copyWith(selectedHub: hub)); + await _loadManagersForHub(hub.id); + } + } + return; + } + } + emit(state.copyWith(eventName: title, date: startDate ?? DateTime.now())); if (orderId == null || orderId.isEmpty) return; diff --git a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/blocs/one_time_order/one_time_order_state.dart b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/blocs/one_time_order/one_time_order_state.dart index b48b9134..c2964f35 100644 --- a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/blocs/one_time_order/one_time_order_state.dart +++ b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/blocs/one_time_order/one_time_order_state.dart @@ -18,6 +18,7 @@ class OneTimeOrderState extends Equatable { this.roles = const [], this.managers = const [], this.selectedManager, + this.isRapidDraft = false, }); factory OneTimeOrderState.initial() { @@ -47,6 +48,7 @@ class OneTimeOrderState extends Equatable { final List roles; final List managers; final OneTimeOrderManagerOption? selectedManager; + final bool isRapidDraft; OneTimeOrderState copyWith({ DateTime? date, @@ -62,6 +64,7 @@ class OneTimeOrderState extends Equatable { List? roles, List? managers, OneTimeOrderManagerOption? selectedManager, + bool? isRapidDraft, }) { return OneTimeOrderState( date: date ?? this.date, @@ -77,6 +80,7 @@ class OneTimeOrderState extends Equatable { roles: roles ?? this.roles, managers: managers ?? this.managers, selectedManager: selectedManager ?? this.selectedManager, + isRapidDraft: isRapidDraft ?? this.isRapidDraft, ); } @@ -109,6 +113,7 @@ class OneTimeOrderState extends Equatable { roles, managers, selectedManager, + isRapidDraft, ]; } @@ -171,10 +176,7 @@ class OneTimeOrderRoleOption extends Equatable { } class OneTimeOrderManagerOption extends Equatable { - const OneTimeOrderManagerOption({ - required this.id, - required this.name, - }); + const OneTimeOrderManagerOption({required this.id, required this.name}); final String id; final String name; @@ -182,4 +184,3 @@ class OneTimeOrderManagerOption extends Equatable { @override List get props => [id, name]; } - diff --git a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/blocs/rapid_order/rapid_order_bloc.dart b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/blocs/rapid_order/rapid_order_bloc.dart index b9fdedf5..3b957100 100644 --- a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/blocs/rapid_order/rapid_order_bloc.dart +++ b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/blocs/rapid_order/rapid_order_bloc.dart @@ -1,7 +1,8 @@ -import 'package:client_create_order/src/domain/arguments/rapid_order_arguments.dart'; -import 'package:client_create_order/src/domain/usecases/create_rapid_order_usecase.dart'; +import 'package:client_create_order/src/domain/usecases/parse_rapid_order_usecase.dart'; +import 'package:client_create_order/src/domain/usecases/transcribe_rapid_order_usecase.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:krow_core/core.dart'; +import 'package:krow_domain/krow_domain.dart'; import 'rapid_order_event.dart'; import 'rapid_order_state.dart'; @@ -9,8 +10,11 @@ import 'rapid_order_state.dart'; /// BLoC for managing the rapid (urgent) order creation flow. class RapidOrderBloc extends Bloc with BlocErrorHandler { - RapidOrderBloc(this._createRapidOrderUseCase) - : super( + RapidOrderBloc( + this._transcribeRapidOrderUseCase, + this._parseRapidOrderUseCase, + this._audioRecorderService, + ) : super( const RapidOrderInitial( examples: [ '"We had a call out. Need 2 cooks ASAP"', @@ -24,7 +28,9 @@ class RapidOrderBloc extends Bloc on(_onSubmitted); on(_onExampleSelected); } - final CreateRapidOrderUseCase _createRapidOrderUseCase; + final TranscribeRapidOrderUseCase _transcribeRapidOrderUseCase; + final ParseRapidOrderTextToOrderUseCase _parseRapidOrderUseCase; + final AudioRecorderService _audioRecorderService; void _onMessageChanged( RapidOrderMessageChanged event, @@ -72,10 +78,8 @@ class RapidOrderBloc extends Bloc await handleError( emit: emit.call, action: () async { - await _createRapidOrderUseCase( - RapidOrderArguments(description: message), - ); - emit(const RapidOrderSuccess()); + final OneTimeOrder order = await _parseRapidOrderUseCase(message); + emit(RapidOrderParsed(order)); }, onError: (String errorKey) => RapidOrderFailure(errorKey), ); diff --git a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/blocs/rapid_order/rapid_order_state.dart b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/blocs/rapid_order/rapid_order_state.dart index 6c752b92..88396c09 100644 --- a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/blocs/rapid_order/rapid_order_state.dart +++ b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/blocs/rapid_order/rapid_order_state.dart @@ -1,4 +1,5 @@ import 'package:equatable/equatable.dart'; +import 'package:krow_domain/krow_domain.dart'; abstract class RapidOrderState extends Equatable { const RapidOrderState(); @@ -48,3 +49,11 @@ class RapidOrderFailure extends RapidOrderState { @override List get props => [error]; } + +class RapidOrderParsed extends RapidOrderState { + const RapidOrderParsed(this.order); + final OneTimeOrder order; + + @override + List get props => [order]; +} diff --git a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/pages/one_time_order_page.dart b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/pages/one_time_order_page.dart index 3c611062..11d8e1d7 100644 --- a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/pages/one_time_order_page.dart +++ b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/pages/one_time_order_page.dart @@ -53,6 +53,7 @@ class OneTimeOrderPage extends StatelessWidget { : null, hubManagers: state.managers.map(_mapManager).toList(), isValid: state.isValid, + title: state.isRapidDraft ? 'Rapid Order : Verify the order' : null, onEventNameChanged: (String val) => bloc.add(OneTimeOrderEventNameChanged(val)), onVendorChanged: (Vendor val) => diff --git a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/rapid_order/rapid_order_view.dart b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/rapid_order/rapid_order_view.dart index 08837105..f15757c2 100644 --- a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/rapid_order/rapid_order_view.dart +++ b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/rapid_order/rapid_order_view.dart @@ -72,6 +72,13 @@ class _RapidOrderFormState extends State<_RapidOrderForm> { TextPosition(offset: _messageController.text.length), ); } + } else if (state is RapidOrderParsed) { + Modular.to.toCreateOrderOneTime( + arguments: { + 'order': state.order, + 'isRapidDraft': true, + }, + ); } else if (state is RapidOrderFailure) { UiSnackbar.show( context, diff --git a/apps/mobile/packages/features/client/orders/orders_common/lib/src/presentation/widgets/one_time_order/one_time_order_view.dart b/apps/mobile/packages/features/client/orders/orders_common/lib/src/presentation/widgets/one_time_order/one_time_order_view.dart index 4abe0eae..bb80fcc4 100644 --- a/apps/mobile/packages/features/client/orders/orders_common/lib/src/presentation/widgets/one_time_order/one_time_order_view.dart +++ b/apps/mobile/packages/features/client/orders/orders_common/lib/src/presentation/widgets/one_time_order/one_time_order_view.dart @@ -38,6 +38,7 @@ class OneTimeOrderView extends StatelessWidget { required this.onSubmit, required this.onDone, required this.onBack, + this.title, super.key, }); @@ -54,6 +55,7 @@ class OneTimeOrderView extends StatelessWidget { final List hubManagers; final OrderManagerUiModel? selectedHubManager; final bool isValid; + final String? title; final ValueChanged onEventNameChanged; final ValueChanged onVendorChanged; @@ -61,7 +63,8 @@ class OneTimeOrderView extends StatelessWidget { final ValueChanged onHubChanged; final ValueChanged onHubManagerChanged; final VoidCallback onPositionAdded; - final void Function(int index, OrderPositionUiModel position) onPositionUpdated; + final void Function(int index, OrderPositionUiModel position) + onPositionUpdated; final void Function(int index) onPositionRemoved; final VoidCallback onSubmit; final VoidCallback onDone; @@ -98,7 +101,7 @@ class OneTimeOrderView extends StatelessWidget { body: Column( children: [ OneTimeOrderHeader( - title: labels.title, + title: title ?? labels.title, subtitle: labels.subtitle, onBack: onBack, ), @@ -136,7 +139,7 @@ class OneTimeOrderView extends StatelessWidget { body: Column( children: [ OneTimeOrderHeader( - title: labels.title, + title: title ?? labels.title, subtitle: labels.subtitle, onBack: onBack, ), @@ -220,7 +223,8 @@ class _OneTimeOrderForm extends StatelessWidget { final ValueChanged onHubChanged; final ValueChanged onHubManagerChanged; final VoidCallback onPositionAdded; - final void Function(int index, OrderPositionUiModel position) onPositionUpdated; + final void Function(int index, OrderPositionUiModel position) + onPositionUpdated; final void Function(int index) onPositionRemoved; @override @@ -317,10 +321,7 @@ class _OneTimeOrderForm extends StatelessWidget { items: hubs.map((OrderHubUiModel hub) { return DropdownMenuItem( value: hub, - child: Text( - hub.name, - style: UiTypography.body2m.textPrimary, - ), + child: Text(hub.name, style: UiTypography.body2m.textPrimary), ); }).toList(), ), diff --git a/apps/mobile/packages/features/client/orders/orders_common/macos/Flutter/GeneratedPluginRegistrant.swift b/apps/mobile/packages/features/client/orders/orders_common/macos/Flutter/GeneratedPluginRegistrant.swift index 8a0af98d..3eb92bc4 100644 --- a/apps/mobile/packages/features/client/orders/orders_common/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/apps/mobile/packages/features/client/orders/orders_common/macos/Flutter/GeneratedPluginRegistrant.swift @@ -10,6 +10,7 @@ import file_selector_macos import firebase_app_check import firebase_auth import firebase_core +import record_macos import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { @@ -18,5 +19,6 @@ 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")) + RecordMacOsPlugin.register(with: registry.registrar(forPlugin: "RecordMacOsPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/apps/mobile/pubspec.lock b/apps/mobile/pubspec.lock index 07839283..7fd533da 100644 --- a/apps/mobile/pubspec.lock +++ b/apps/mobile/pubspec.lock @@ -1221,6 +1221,70 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.0" + record: + dependency: transitive + description: + name: record + sha256: d5b6b334f3ab02460db6544e08583c942dbf23e3504bf1e14fd4cbe3d9409277 + url: "https://pub.dev" + source: hosted + version: "6.2.0" + record_android: + dependency: transitive + description: + name: record_android + sha256: "94783f08403aed33ffb68797bf0715b0812eb852f3c7985644c945faea462ba1" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + record_ios: + dependency: transitive + description: + name: record_ios + sha256: "8df7c136131bd05efc19256af29b2ba6ccc000ccc2c80d4b6b6d7a8d21a3b5a9" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + record_linux: + dependency: transitive + description: + name: record_linux + sha256: c31a35cc158cd666fc6395f7f56fc054f31685571684be6b97670a27649ce5c7 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + record_macos: + dependency: transitive + description: + name: record_macos + sha256: "084902e63fc9c0c224c29203d6c75f0bdf9b6a40536c9d916393c8f4c4256488" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + record_platform_interface: + dependency: transitive + description: + name: record_platform_interface + sha256: "8a81dbc4e14e1272a285bbfef6c9136d070a47d9b0d1f40aa6193516253ee2f6" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + record_web: + dependency: transitive + description: + name: record_web + sha256: "7e9846981c1f2d111d86f0ae3309071f5bba8b624d1c977316706f08fc31d16d" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + record_windows: + dependency: transitive + description: + name: record_windows + sha256: "223258060a1d25c62bae18282c16783f28581ec19401d17e56b5205b9f039d78" + url: "https://pub.dev" + source: hosted + version: "1.0.7" rename: dependency: transitive description: From c52bb7e767cc3f6fe2499fecf91426ebfd844a80 Mon Sep 17 00:00:00 2001 From: zouantchaw <44246692+zouantchaw@users.noreply.github.com> Date: Fri, 27 Feb 2026 15:28:09 -0500 Subject: [PATCH 2/2] fix(dataconnect): split fast connector sync from full schema sync --- Makefile | 5 ++-- .../DOCUMENTS/backend_manual.md | 24 ++++++++++++++---- .../documents/data connect/backend_manual.md | 24 ++++++++++++++---- makefiles/dataconnect.mk | 25 +++++++++++++------ 4 files changed, 59 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index 71d0b18d..4a029884 100644 --- a/Makefile +++ b/Makefile @@ -66,7 +66,8 @@ help: @echo " make dataconnect-deploy [ENV=dev] Deploy Data Connect schemas (dev/staging)" @echo " make dataconnect-sql-migrate [ENV=dev] Apply pending SQL migrations" @echo " make dataconnect-generate-sdk [ENV=dev] Regenerate Data Connect client SDK" - @echo " make dataconnect-sync [ENV=dev] Full sync: deploy + migrate + generate SDK" + @echo " make dataconnect-sync [ENV=dev] Fast sync: deploy connector + generate SDK" + @echo " make dataconnect-sync-full [ENV=dev] Full sync: deploy + migrate + generate SDK" @echo " make dataconnect-seed [ENV=dev] Seed database with test data" @echo " make dataconnect-clean [ENV=dev] Delete all data from Data Connect" @echo " make dataconnect-test [ENV=dev] Test Data Connect deployment (dry-run)" @@ -101,6 +102,6 @@ help: @echo "" @echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" @echo " 💡 Tip: Run 'make mobile-install' first for mobile development" - @echo " 💡 Tip: Use 'make dataconnect-sync' after schema changes" + @echo " 💡 Tip: Use 'make dataconnect-sync-full' after schema changes" @echo " 💡 Tip: Default ENV=dev, use ENV=staging for staging environment" @echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" diff --git a/docs/BACKEND/DATACONNECT_GUIDES/DOCUMENTS/backend_manual.md b/docs/BACKEND/DATACONNECT_GUIDES/DOCUMENTS/backend_manual.md index c31b0f8c..90e1dead 100644 --- a/docs/BACKEND/DATACONNECT_GUIDES/DOCUMENTS/backend_manual.md +++ b/docs/BACKEND/DATACONNECT_GUIDES/DOCUMENTS/backend_manual.md @@ -186,7 +186,8 @@ client.createInvoice(); | dataconnect-deploy | Deploy schemas | | dataconnect-sql-migrate | Apply DB migrations | | dataconnect-generate-sdk | Generate SDK | -| dataconnect-sync | Full backend update | +| dataconnect-sync | Fast connector + SDK sync | +| dataconnect-sync-full | Full backend update | | dataconnect-test | Test without breaking | | dataconnect-seed | Insert seed data | | dataconnect-bootstrap-db | Create Cloud SQL | @@ -195,7 +196,7 @@ client.createInvoice(); ## 9. Correct Backend Workflow -### Production Flow +### Query/Connector Flow ```bash make dataconnect-sync @@ -203,9 +204,22 @@ make dataconnect-sync Steps: -1. Deploy schema -2. Run SQL migrations -3. Generate SDK +1. Deploy connector +2. Generate SDK + +--- + +### Full Schema Flow + +```bash +make dataconnect-sync-full +``` + +Steps: + +1. Deploy schema +2. Run SQL migrations +3. Generate SDK --- diff --git a/internal/launchpad/assets/documents/data connect/backend_manual.md b/internal/launchpad/assets/documents/data connect/backend_manual.md index c31b0f8c..90e1dead 100644 --- a/internal/launchpad/assets/documents/data connect/backend_manual.md +++ b/internal/launchpad/assets/documents/data connect/backend_manual.md @@ -186,7 +186,8 @@ client.createInvoice(); | dataconnect-deploy | Deploy schemas | | dataconnect-sql-migrate | Apply DB migrations | | dataconnect-generate-sdk | Generate SDK | -| dataconnect-sync | Full backend update | +| dataconnect-sync | Fast connector + SDK sync | +| dataconnect-sync-full | Full backend update | | dataconnect-test | Test without breaking | | dataconnect-seed | Insert seed data | | dataconnect-bootstrap-db | Create Cloud SQL | @@ -195,7 +196,7 @@ client.createInvoice(); ## 9. Correct Backend Workflow -### Production Flow +### Query/Connector Flow ```bash make dataconnect-sync @@ -203,9 +204,22 @@ make dataconnect-sync Steps: -1. Deploy schema -2. Run SQL migrations -3. Generate SDK +1. Deploy connector +2. Generate SDK + +--- + +### Full Schema Flow + +```bash +make dataconnect-sync-full +``` + +Steps: + +1. Deploy schema +2. Run SQL migrations +3. Generate SDK --- diff --git a/makefiles/dataconnect.mk b/makefiles/dataconnect.mk index 3ae3410e..4c9f7ef9 100644 --- a/makefiles/dataconnect.mk +++ b/makefiles/dataconnect.mk @@ -2,11 +2,14 @@ # Usage examples: # make dataconnect-sync DC_ENV=dev +# make dataconnect-sync-full DC_ENV=dev # make dataconnect-seed DC_ENV=validation # make dataconnect-clean DC_ENV=validation # make dataconnect-generate-sdk DC_ENV=dev # DC_ENV ?= dev +DC_LOCATION ?= us-central1 +DC_CONNECTOR_ID ?= example DC_SERVICE_DEV := krow-workforce-db DC_SERVICE_VALIDATION := krow-workforce-db-validation @@ -19,7 +22,7 @@ else $(error Invalid DC_ENV '$(DC_ENV)'. Use DC_ENV=dev or DC_ENV=validation) endif -.PHONY: dataconnect-enable-apis dataconnect-init dataconnect-deploy dataconnect-sql-migrate dataconnect-generate-sdk dataconnect-sync dataconnect-bootstrap-db check-gcloud-beta dataconnect-clean dataconnect-bootstrap-validation-db dataconnect-file dataconnect-file-validation dataconnect-file-dev dataconnect-seed dataconnect-test +.PHONY: dataconnect-enable-apis dataconnect-init dataconnect-deploy dataconnect-sql-migrate dataconnect-generate-sdk dataconnect-sync dataconnect-sync-full dataconnect-bootstrap-db check-gcloud-beta dataconnect-clean dataconnect-bootstrap-validation-db dataconnect-file dataconnect-file-validation dataconnect-file-dev dataconnect-seed dataconnect-test #creation dataconnect file dataconnect-file: @@ -79,15 +82,23 @@ dataconnect-generate-sdk: dataconnect-file @firebase dataconnect:sdk:generate --project=$(FIREBASE_ALIAS) @echo "✅ Data Connect SDK generation completed for [$(DC_ENV)]." -# Unified backend schema update workflow (schema -> deploy -> SDK) +# Fast sync workflow for connector/query changes (deploy connector -> SDK) dataconnect-sync: dataconnect-file - @echo "--> [1/3] Deploying Data Connect [$(DC_SERVICE)]..." - @firebase deploy --only dataconnect:$(DC_SERVICE) --project=$(FIREBASE_ALIAS) + @echo "--> [1/2] Deploying Data Connect connector [$(DC_SERVICE):$(DC_CONNECTOR_ID)]..." + @firebase deploy --only dataconnect:$(DC_SERVICE):$(DC_CONNECTOR_ID) --project=$(FIREBASE_ALIAS) --force + @echo "--> [2/2] Regenerating SDK [$(DC_SERVICE)]..." + @firebase dataconnect:sdk:generate --project=$(FIREBASE_ALIAS) + @echo "✅ Data Connect connector + SDK sync completed for [$(DC_ENV)]." + +# Full backend schema update workflow (schema -> migrate -> SDK) +dataconnect-sync-full: dataconnect-file + @echo "--> [1/3] Deploying full Data Connect service [$(DC_SERVICE)]..." + @firebase deploy --only dataconnect:$(DC_SERVICE) --project=$(FIREBASE_ALIAS) --force @echo "--> [2/3] Applying SQL migrations [$(DC_SERVICE)]..." - @firebase dataconnect:sql:migrate --project=$(FIREBASE_ALIAS) + @firebase dataconnect:sql:migrate --project=$(FIREBASE_ALIAS) --service $(DC_SERVICE) --location $(DC_LOCATION) --force @echo "--> [3/3] Regenerating SDK [$(DC_SERVICE)]..." @firebase dataconnect:sdk:generate --project=$(FIREBASE_ALIAS) - @echo "✅ Data Connect SQL, deploy, and SDK generation [$(ENV)]." + @echo "✅ Data Connect full schema sync completed for [$(DC_ENV)]." # Execute seed in Firebase Data Connect dataconnect-seed: dataconnect-file @@ -268,4 +279,4 @@ dataconnect-bootstrap-validation-database: dataconnect-file-validation @echo "⚠️ Generating Data Connect SDK ($(DC_SERVICE))..." @firebase dataconnect:sdk:generate --project=$(FIREBASE_ALIAS) - @echo "🎉 Validation Cloud SQL + Data Connect bootstrap completed successfully!" \ No newline at end of file + @echo "🎉 Validation Cloud SQL + Data Connect bootstrap completed successfully!"