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.
This commit is contained in:
@@ -36,6 +36,12 @@
|
|||||||
@import image_picker_ios;
|
@import image_picker_ios;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<record_ios/RecordIosPlugin.h>)
|
||||||
|
#import <record_ios/RecordIosPlugin.h>
|
||||||
|
#else
|
||||||
|
@import record_ios;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if __has_include(<shared_preferences_foundation/SharedPreferencesPlugin.h>)
|
#if __has_include(<shared_preferences_foundation/SharedPreferencesPlugin.h>)
|
||||||
#import <shared_preferences_foundation/SharedPreferencesPlugin.h>
|
#import <shared_preferences_foundation/SharedPreferencesPlugin.h>
|
||||||
#else
|
#else
|
||||||
@@ -56,6 +62,7 @@
|
|||||||
[FLTFirebaseAuthPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseAuthPlugin"]];
|
[FLTFirebaseAuthPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseAuthPlugin"]];
|
||||||
[FLTFirebaseCorePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseCorePlugin"]];
|
[FLTFirebaseCorePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseCorePlugin"]];
|
||||||
[FLTImagePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTImagePickerPlugin"]];
|
[FLTImagePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTImagePickerPlugin"]];
|
||||||
|
[RecordIosPlugin registerWithRegistrar:[registry registrarForPlugin:@"RecordIosPlugin"]];
|
||||||
[SharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"SharedPreferencesPlugin"]];
|
[SharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"SharedPreferencesPlugin"]];
|
||||||
[URLLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"URLLauncherPlugin"]];
|
[URLLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"URLLauncherPlugin"]];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import file_selector_macos
|
|||||||
import firebase_app_check
|
import firebase_app_check
|
||||||
import firebase_auth
|
import firebase_auth
|
||||||
import firebase_core
|
import firebase_core
|
||||||
|
import record_macos
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
FLTFirebaseAppCheckPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAppCheckPlugin"))
|
FLTFirebaseAppCheckPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAppCheckPlugin"))
|
||||||
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
|
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
|
||||||
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
||||||
|
RecordMacOsPlugin.register(with: registry.registrar(forPlugin: "RecordMacOsPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,12 @@
|
|||||||
@import permission_handler_apple;
|
@import permission_handler_apple;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<record_ios/RecordIosPlugin.h>)
|
||||||
|
#import <record_ios/RecordIosPlugin.h>
|
||||||
|
#else
|
||||||
|
@import record_ios;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if __has_include(<shared_preferences_foundation/SharedPreferencesPlugin.h>)
|
#if __has_include(<shared_preferences_foundation/SharedPreferencesPlugin.h>)
|
||||||
#import <shared_preferences_foundation/SharedPreferencesPlugin.h>
|
#import <shared_preferences_foundation/SharedPreferencesPlugin.h>
|
||||||
#else
|
#else
|
||||||
@@ -77,6 +83,7 @@
|
|||||||
[FLTGoogleMapsPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTGoogleMapsPlugin"]];
|
[FLTGoogleMapsPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTGoogleMapsPlugin"]];
|
||||||
[FLTImagePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTImagePickerPlugin"]];
|
[FLTImagePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTImagePickerPlugin"]];
|
||||||
[PermissionHandlerPlugin registerWithRegistrar:[registry registrarForPlugin:@"PermissionHandlerPlugin"]];
|
[PermissionHandlerPlugin registerWithRegistrar:[registry registrarForPlugin:@"PermissionHandlerPlugin"]];
|
||||||
|
[RecordIosPlugin registerWithRegistrar:[registry registrarForPlugin:@"RecordIosPlugin"]];
|
||||||
[SharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"SharedPreferencesPlugin"]];
|
[SharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"SharedPreferencesPlugin"]];
|
||||||
[URLLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"URLLauncherPlugin"]];
|
[URLLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"URLLauncherPlugin"]];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import firebase_app_check
|
|||||||
import firebase_auth
|
import firebase_auth
|
||||||
import firebase_core
|
import firebase_core
|
||||||
import geolocator_apple
|
import geolocator_apple
|
||||||
|
import record_macos
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
|
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
|
||||||
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
||||||
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
|
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
|
||||||
|
RecordMacOsPlugin.register(with: registry.registrar(forPlugin: "RecordMacOsPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ extension StaffNavigator on IModularNavigator {
|
|||||||
///
|
///
|
||||||
/// Manage personal information, documents, and preferences.
|
/// Manage personal information, documents, and preferences.
|
||||||
void toProfile() {
|
void toProfile() {
|
||||||
pushNamedAndRemoveUntil(StaffPaths.profile, (_) => false);
|
navigate(StaffPaths.profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
@@ -189,7 +189,7 @@ extension StaffNavigator on IModularNavigator {
|
|||||||
///
|
///
|
||||||
/// Record previous work experience and qualifications.
|
/// Record previous work experience and qualifications.
|
||||||
void toExperience() {
|
void toExperience() {
|
||||||
pushNamed(StaffPaths.experience);
|
navigate(StaffPaths.experience);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pushes the attire preferences page.
|
/// Pushes the attire preferences page.
|
||||||
|
|||||||
@@ -25,4 +25,5 @@ dependencies:
|
|||||||
image_picker: ^1.1.2
|
image_picker: ^1.1.2
|
||||||
path_provider: ^2.1.3
|
path_provider: ^2.1.3
|
||||||
file_picker: ^8.1.7
|
file_picker: ^8.1.7
|
||||||
|
record: ^6.2.0
|
||||||
firebase_auth: ^6.1.4
|
firebase_auth: ^6.1.4
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import 'domain/usecases/create_permanent_order_usecase.dart';
|
|||||||
import 'domain/usecases/create_recurring_order_usecase.dart';
|
import 'domain/usecases/create_recurring_order_usecase.dart';
|
||||||
import 'domain/usecases/create_rapid_order_usecase.dart';
|
import 'domain/usecases/create_rapid_order_usecase.dart';
|
||||||
import 'domain/usecases/get_order_details_for_reorder_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/blocs/index.dart';
|
||||||
import 'presentation/pages/create_order_page.dart';
|
import 'presentation/pages/create_order_page.dart';
|
||||||
import 'presentation/pages/one_time_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(CreatePermanentOrderUseCase.new);
|
||||||
i.addLazySingleton(CreateRecurringOrderUseCase.new);
|
i.addLazySingleton(CreateRecurringOrderUseCase.new);
|
||||||
i.addLazySingleton(CreateRapidOrderUseCase.new);
|
i.addLazySingleton(CreateRapidOrderUseCase.new);
|
||||||
|
i.addLazySingleton(TranscribeRapidOrderUseCase.new);
|
||||||
|
i.addLazySingleton(ParseRapidOrderTextToOrderUseCase.new);
|
||||||
i.addLazySingleton(GetOrderDetailsForReorderUseCase.new);
|
i.addLazySingleton(GetOrderDetailsForReorderUseCase.new);
|
||||||
|
|
||||||
// BLoCs
|
// BLoCs
|
||||||
i.add<RapidOrderBloc>(RapidOrderBloc.new);
|
i.add<RapidOrderBloc>(
|
||||||
|
(Injector i) => RapidOrderBloc(
|
||||||
|
i.get<TranscribeRapidOrderUseCase>(),
|
||||||
|
i.get<ParseRapidOrderTextToOrderUseCase>(),
|
||||||
|
i.get<AudioRecorderService>(),
|
||||||
|
),
|
||||||
|
);
|
||||||
i.add<OneTimeOrderBloc>(OneTimeOrderBloc.new);
|
i.add<OneTimeOrderBloc>(OneTimeOrderBloc.new);
|
||||||
i.add<PermanentOrderBloc>(PermanentOrderBloc.new);
|
i.add<PermanentOrderBloc>(PermanentOrderBloc.new);
|
||||||
i.add<RecurringOrderBloc>(RecurringOrderBloc.new);
|
i.add<RecurringOrderBloc>(RecurringOrderBloc.new);
|
||||||
|
|||||||
@@ -367,6 +367,51 @@ class ClientCreateOrderRepositoryImpl
|
|||||||
throw UnimplementedError('Rapid order IA is not connected yet.');
|
throw UnimplementedError('Rapid order IA is not connected yet.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<domain.OneTimeOrder> 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<String> transcribeRapidOrder(String audioPath) async {
|
||||||
|
final RapidOrderTranscriptionResponse response = await _rapidOrderService
|
||||||
|
.transcribeAudio(audioFileUri: audioPath);
|
||||||
|
return response.transcript;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> reorder(String previousOrderId, DateTime newDate) async {
|
Future<void> reorder(String previousOrderId, DateTime newDate) async {
|
||||||
// TODO: Implement reorder functionality to fetch the previous order and create a new one with the updated date.
|
// TODO: Implement reorder functionality to fetch the previous order and create a new one with the updated date.
|
||||||
|
|||||||
@@ -24,6 +24,16 @@ abstract interface class ClientCreateOrderRepositoryInterface {
|
|||||||
/// [description] is the text message (or transcribed voice) describing the need.
|
/// [description] is the text message (or transcribed voice) describing the need.
|
||||||
Future<void> createRapidOrder(String description);
|
Future<void> createRapidOrder(String description);
|
||||||
|
|
||||||
|
/// Transcribes the audio file for a rapid order.
|
||||||
|
///
|
||||||
|
/// [audioPath] is the local path to the recorded audio file.
|
||||||
|
Future<String> transcribeRapidOrder(String audioPath);
|
||||||
|
|
||||||
|
/// Parses the text description for a rapid order into a structured draft.
|
||||||
|
///
|
||||||
|
/// [text] is the text message describing the need.
|
||||||
|
Future<OneTimeOrder> parseRapidOrder(String text);
|
||||||
|
|
||||||
/// Reorders an existing staffing order with a new date.
|
/// Reorders an existing staffing order with a new date.
|
||||||
///
|
///
|
||||||
/// [previousOrderId] is the ID of the order to reorder.
|
/// [previousOrderId] is the ID of the order to reorder.
|
||||||
|
|||||||
@@ -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<OneTimeOrder> call(String text) async {
|
||||||
|
return _repository.parseRapidOrder(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -136,9 +136,7 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadManagersForHub(
|
Future<void> _loadManagersForHub(String hubId) async {
|
||||||
String hubId,
|
|
||||||
) async {
|
|
||||||
final List<OneTimeOrderManagerOption>? managers =
|
final List<OneTimeOrderManagerOption>? managers =
|
||||||
await handleErrorWithResult(
|
await handleErrorWithResult(
|
||||||
action: () async {
|
action: () async {
|
||||||
@@ -163,7 +161,9 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
|
|||||||
.toList();
|
.toList();
|
||||||
},
|
},
|
||||||
onError: (_) {
|
onError: (_) {
|
||||||
add(const OneTimeOrderManagersLoaded(<OneTimeOrderManagerOption>[]));
|
add(
|
||||||
|
const OneTimeOrderManagersLoaded(<OneTimeOrderManagerOption>[]),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -172,7 +172,6 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Future<void> _onVendorsLoaded(
|
Future<void> _onVendorsLoaded(
|
||||||
OneTimeOrderVendorsLoaded event,
|
OneTimeOrderVendorsLoaded event,
|
||||||
Emitter<OneTimeOrderState> emit,
|
Emitter<OneTimeOrderState> emit,
|
||||||
@@ -216,7 +215,6 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void _onHubChanged(
|
void _onHubChanged(
|
||||||
OneTimeOrderHubChanged event,
|
OneTimeOrderHubChanged event,
|
||||||
Emitter<OneTimeOrderState> emit,
|
Emitter<OneTimeOrderState> emit,
|
||||||
@@ -239,7 +237,6 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
|
|||||||
emit(state.copyWith(managers: event.managers));
|
emit(state.copyWith(managers: event.managers));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void _onEventNameChanged(
|
void _onEventNameChanged(
|
||||||
OneTimeOrderEventNameChanged event,
|
OneTimeOrderEventNameChanged event,
|
||||||
Emitter<OneTimeOrderState> emit,
|
Emitter<OneTimeOrderState> emit,
|
||||||
@@ -349,6 +346,45 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
|
|||||||
final DateTime? startDate = data['startDate'] as DateTime?;
|
final DateTime? startDate = data['startDate'] as DateTime?;
|
||||||
final String? orderId = data['orderId']?.toString();
|
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()));
|
emit(state.copyWith(eventName: title, date: startDate ?? DateTime.now()));
|
||||||
|
|
||||||
if (orderId == null || orderId.isEmpty) return;
|
if (orderId == null || orderId.isEmpty) return;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class OneTimeOrderState extends Equatable {
|
|||||||
this.roles = const <OneTimeOrderRoleOption>[],
|
this.roles = const <OneTimeOrderRoleOption>[],
|
||||||
this.managers = const <OneTimeOrderManagerOption>[],
|
this.managers = const <OneTimeOrderManagerOption>[],
|
||||||
this.selectedManager,
|
this.selectedManager,
|
||||||
|
this.isRapidDraft = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory OneTimeOrderState.initial() {
|
factory OneTimeOrderState.initial() {
|
||||||
@@ -47,6 +48,7 @@ class OneTimeOrderState extends Equatable {
|
|||||||
final List<OneTimeOrderRoleOption> roles;
|
final List<OneTimeOrderRoleOption> roles;
|
||||||
final List<OneTimeOrderManagerOption> managers;
|
final List<OneTimeOrderManagerOption> managers;
|
||||||
final OneTimeOrderManagerOption? selectedManager;
|
final OneTimeOrderManagerOption? selectedManager;
|
||||||
|
final bool isRapidDraft;
|
||||||
|
|
||||||
OneTimeOrderState copyWith({
|
OneTimeOrderState copyWith({
|
||||||
DateTime? date,
|
DateTime? date,
|
||||||
@@ -62,6 +64,7 @@ class OneTimeOrderState extends Equatable {
|
|||||||
List<OneTimeOrderRoleOption>? roles,
|
List<OneTimeOrderRoleOption>? roles,
|
||||||
List<OneTimeOrderManagerOption>? managers,
|
List<OneTimeOrderManagerOption>? managers,
|
||||||
OneTimeOrderManagerOption? selectedManager,
|
OneTimeOrderManagerOption? selectedManager,
|
||||||
|
bool? isRapidDraft,
|
||||||
}) {
|
}) {
|
||||||
return OneTimeOrderState(
|
return OneTimeOrderState(
|
||||||
date: date ?? this.date,
|
date: date ?? this.date,
|
||||||
@@ -77,6 +80,7 @@ class OneTimeOrderState extends Equatable {
|
|||||||
roles: roles ?? this.roles,
|
roles: roles ?? this.roles,
|
||||||
managers: managers ?? this.managers,
|
managers: managers ?? this.managers,
|
||||||
selectedManager: selectedManager ?? this.selectedManager,
|
selectedManager: selectedManager ?? this.selectedManager,
|
||||||
|
isRapidDraft: isRapidDraft ?? this.isRapidDraft,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +113,7 @@ class OneTimeOrderState extends Equatable {
|
|||||||
roles,
|
roles,
|
||||||
managers,
|
managers,
|
||||||
selectedManager,
|
selectedManager,
|
||||||
|
isRapidDraft,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,10 +176,7 @@ class OneTimeOrderRoleOption extends Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class OneTimeOrderManagerOption extends Equatable {
|
class OneTimeOrderManagerOption extends Equatable {
|
||||||
const OneTimeOrderManagerOption({
|
const OneTimeOrderManagerOption({required this.id, required this.name});
|
||||||
required this.id,
|
|
||||||
required this.name,
|
|
||||||
});
|
|
||||||
|
|
||||||
final String id;
|
final String id;
|
||||||
final String name;
|
final String name;
|
||||||
@@ -182,4 +184,3 @@ class OneTimeOrderManagerOption extends Equatable {
|
|||||||
@override
|
@override
|
||||||
List<Object?> get props => <Object?>[id, name];
|
List<Object?> get props => <Object?>[id, name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import 'package:client_create_order/src/domain/arguments/rapid_order_arguments.dart';
|
import 'package:client_create_order/src/domain/usecases/parse_rapid_order_usecase.dart';
|
||||||
import 'package:client_create_order/src/domain/usecases/create_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:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:krow_core/core.dart';
|
import 'package:krow_core/core.dart';
|
||||||
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
|
||||||
import 'rapid_order_event.dart';
|
import 'rapid_order_event.dart';
|
||||||
import 'rapid_order_state.dart';
|
import 'rapid_order_state.dart';
|
||||||
@@ -9,8 +10,11 @@ import 'rapid_order_state.dart';
|
|||||||
/// BLoC for managing the rapid (urgent) order creation flow.
|
/// BLoC for managing the rapid (urgent) order creation flow.
|
||||||
class RapidOrderBloc extends Bloc<RapidOrderEvent, RapidOrderState>
|
class RapidOrderBloc extends Bloc<RapidOrderEvent, RapidOrderState>
|
||||||
with BlocErrorHandler<RapidOrderState> {
|
with BlocErrorHandler<RapidOrderState> {
|
||||||
RapidOrderBloc(this._createRapidOrderUseCase)
|
RapidOrderBloc(
|
||||||
: super(
|
this._transcribeRapidOrderUseCase,
|
||||||
|
this._parseRapidOrderUseCase,
|
||||||
|
this._audioRecorderService,
|
||||||
|
) : super(
|
||||||
const RapidOrderInitial(
|
const RapidOrderInitial(
|
||||||
examples: <String>[
|
examples: <String>[
|
||||||
'"We had a call out. Need 2 cooks ASAP"',
|
'"We had a call out. Need 2 cooks ASAP"',
|
||||||
@@ -24,7 +28,9 @@ class RapidOrderBloc extends Bloc<RapidOrderEvent, RapidOrderState>
|
|||||||
on<RapidOrderSubmitted>(_onSubmitted);
|
on<RapidOrderSubmitted>(_onSubmitted);
|
||||||
on<RapidOrderExampleSelected>(_onExampleSelected);
|
on<RapidOrderExampleSelected>(_onExampleSelected);
|
||||||
}
|
}
|
||||||
final CreateRapidOrderUseCase _createRapidOrderUseCase;
|
final TranscribeRapidOrderUseCase _transcribeRapidOrderUseCase;
|
||||||
|
final ParseRapidOrderTextToOrderUseCase _parseRapidOrderUseCase;
|
||||||
|
final AudioRecorderService _audioRecorderService;
|
||||||
|
|
||||||
void _onMessageChanged(
|
void _onMessageChanged(
|
||||||
RapidOrderMessageChanged event,
|
RapidOrderMessageChanged event,
|
||||||
@@ -72,10 +78,8 @@ class RapidOrderBloc extends Bloc<RapidOrderEvent, RapidOrderState>
|
|||||||
await handleError(
|
await handleError(
|
||||||
emit: emit.call,
|
emit: emit.call,
|
||||||
action: () async {
|
action: () async {
|
||||||
await _createRapidOrderUseCase(
|
final OneTimeOrder order = await _parseRapidOrderUseCase(message);
|
||||||
RapidOrderArguments(description: message),
|
emit(RapidOrderParsed(order));
|
||||||
);
|
|
||||||
emit(const RapidOrderSuccess());
|
|
||||||
},
|
},
|
||||||
onError: (String errorKey) => RapidOrderFailure(errorKey),
|
onError: (String errorKey) => RapidOrderFailure(errorKey),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
|
||||||
abstract class RapidOrderState extends Equatable {
|
abstract class RapidOrderState extends Equatable {
|
||||||
const RapidOrderState();
|
const RapidOrderState();
|
||||||
@@ -48,3 +49,11 @@ class RapidOrderFailure extends RapidOrderState {
|
|||||||
@override
|
@override
|
||||||
List<Object?> get props => <Object?>[error];
|
List<Object?> get props => <Object?>[error];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RapidOrderParsed extends RapidOrderState {
|
||||||
|
const RapidOrderParsed(this.order);
|
||||||
|
final OneTimeOrder order;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => <Object?>[order];
|
||||||
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class OneTimeOrderPage extends StatelessWidget {
|
|||||||
: null,
|
: null,
|
||||||
hubManagers: state.managers.map(_mapManager).toList(),
|
hubManagers: state.managers.map(_mapManager).toList(),
|
||||||
isValid: state.isValid,
|
isValid: state.isValid,
|
||||||
|
title: state.isRapidDraft ? 'Rapid Order : Verify the order' : null,
|
||||||
onEventNameChanged: (String val) =>
|
onEventNameChanged: (String val) =>
|
||||||
bloc.add(OneTimeOrderEventNameChanged(val)),
|
bloc.add(OneTimeOrderEventNameChanged(val)),
|
||||||
onVendorChanged: (Vendor val) =>
|
onVendorChanged: (Vendor val) =>
|
||||||
|
|||||||
@@ -72,6 +72,13 @@ class _RapidOrderFormState extends State<_RapidOrderForm> {
|
|||||||
TextPosition(offset: _messageController.text.length),
|
TextPosition(offset: _messageController.text.length),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else if (state is RapidOrderParsed) {
|
||||||
|
Modular.to.toCreateOrderOneTime(
|
||||||
|
arguments: <String, dynamic>{
|
||||||
|
'order': state.order,
|
||||||
|
'isRapidDraft': true,
|
||||||
|
},
|
||||||
|
);
|
||||||
} else if (state is RapidOrderFailure) {
|
} else if (state is RapidOrderFailure) {
|
||||||
UiSnackbar.show(
|
UiSnackbar.show(
|
||||||
context,
|
context,
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ class OneTimeOrderView extends StatelessWidget {
|
|||||||
required this.onSubmit,
|
required this.onSubmit,
|
||||||
required this.onDone,
|
required this.onDone,
|
||||||
required this.onBack,
|
required this.onBack,
|
||||||
|
this.title,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -54,6 +55,7 @@ class OneTimeOrderView extends StatelessWidget {
|
|||||||
final List<OrderManagerUiModel> hubManagers;
|
final List<OrderManagerUiModel> hubManagers;
|
||||||
final OrderManagerUiModel? selectedHubManager;
|
final OrderManagerUiModel? selectedHubManager;
|
||||||
final bool isValid;
|
final bool isValid;
|
||||||
|
final String? title;
|
||||||
|
|
||||||
final ValueChanged<String> onEventNameChanged;
|
final ValueChanged<String> onEventNameChanged;
|
||||||
final ValueChanged<Vendor> onVendorChanged;
|
final ValueChanged<Vendor> onVendorChanged;
|
||||||
@@ -61,7 +63,8 @@ class OneTimeOrderView extends StatelessWidget {
|
|||||||
final ValueChanged<OrderHubUiModel> onHubChanged;
|
final ValueChanged<OrderHubUiModel> onHubChanged;
|
||||||
final ValueChanged<OrderManagerUiModel?> onHubManagerChanged;
|
final ValueChanged<OrderManagerUiModel?> onHubManagerChanged;
|
||||||
final VoidCallback onPositionAdded;
|
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 void Function(int index) onPositionRemoved;
|
||||||
final VoidCallback onSubmit;
|
final VoidCallback onSubmit;
|
||||||
final VoidCallback onDone;
|
final VoidCallback onDone;
|
||||||
@@ -98,7 +101,7 @@ class OneTimeOrderView extends StatelessWidget {
|
|||||||
body: Column(
|
body: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
OneTimeOrderHeader(
|
OneTimeOrderHeader(
|
||||||
title: labels.title,
|
title: title ?? labels.title,
|
||||||
subtitle: labels.subtitle,
|
subtitle: labels.subtitle,
|
||||||
onBack: onBack,
|
onBack: onBack,
|
||||||
),
|
),
|
||||||
@@ -136,7 +139,7 @@ class OneTimeOrderView extends StatelessWidget {
|
|||||||
body: Column(
|
body: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
OneTimeOrderHeader(
|
OneTimeOrderHeader(
|
||||||
title: labels.title,
|
title: title ?? labels.title,
|
||||||
subtitle: labels.subtitle,
|
subtitle: labels.subtitle,
|
||||||
onBack: onBack,
|
onBack: onBack,
|
||||||
),
|
),
|
||||||
@@ -220,7 +223,8 @@ class _OneTimeOrderForm extends StatelessWidget {
|
|||||||
final ValueChanged<OrderHubUiModel> onHubChanged;
|
final ValueChanged<OrderHubUiModel> onHubChanged;
|
||||||
final ValueChanged<OrderManagerUiModel?> onHubManagerChanged;
|
final ValueChanged<OrderManagerUiModel?> onHubManagerChanged;
|
||||||
final VoidCallback onPositionAdded;
|
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 void Function(int index) onPositionRemoved;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -317,10 +321,7 @@ class _OneTimeOrderForm extends StatelessWidget {
|
|||||||
items: hubs.map((OrderHubUiModel hub) {
|
items: hubs.map((OrderHubUiModel hub) {
|
||||||
return DropdownMenuItem<OrderHubUiModel>(
|
return DropdownMenuItem<OrderHubUiModel>(
|
||||||
value: hub,
|
value: hub,
|
||||||
child: Text(
|
child: Text(hub.name, style: UiTypography.body2m.textPrimary),
|
||||||
hub.name,
|
|
||||||
style: UiTypography.body2m.textPrimary,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import file_selector_macos
|
|||||||
import firebase_app_check
|
import firebase_app_check
|
||||||
import firebase_auth
|
import firebase_auth
|
||||||
import firebase_core
|
import firebase_core
|
||||||
|
import record_macos
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
@@ -18,5 +19,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
FLTFirebaseAppCheckPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAppCheckPlugin"))
|
FLTFirebaseAppCheckPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAppCheckPlugin"))
|
||||||
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
|
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
|
||||||
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
||||||
|
RecordMacOsPlugin.register(with: registry.registrar(forPlugin: "RecordMacOsPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1221,6 +1221,70 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.0"
|
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:
|
rename:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
Reference in New Issue
Block a user