Merge dev into feature branch

This commit is contained in:
2026-03-19 13:16:04 +05:30
273 changed files with 7867 additions and 3654 deletions

View File

@@ -11,7 +11,11 @@ import 'domain/usecases/create_one_time_order_usecase.dart';
import 'domain/usecases/create_permanent_order_usecase.dart';
import 'domain/usecases/create_rapid_order_usecase.dart';
import 'domain/usecases/create_recurring_order_usecase.dart';
import 'domain/usecases/get_hubs_usecase.dart';
import 'domain/usecases/get_managers_by_hub_usecase.dart';
import 'domain/usecases/get_order_details_for_reorder_usecase.dart';
import 'domain/usecases/get_roles_by_vendor_usecase.dart';
import 'domain/usecases/get_vendors_usecase.dart';
import 'domain/usecases/parse_rapid_order_usecase.dart';
import 'domain/usecases/transcribe_rapid_order_usecase.dart';
import 'presentation/blocs/index.dart';
@@ -46,7 +50,7 @@ class ClientCreateOrderModule extends Module {
),
);
// UseCases
// Command UseCases (order creation)
i.addLazySingleton(CreateOneTimeOrderUseCase.new);
i.addLazySingleton(CreatePermanentOrderUseCase.new);
i.addLazySingleton(CreateRecurringOrderUseCase.new);
@@ -55,6 +59,12 @@ class ClientCreateOrderModule extends Module {
i.addLazySingleton(ParseRapidOrderTextToOrderUseCase.new);
i.addLazySingleton(GetOrderDetailsForReorderUseCase.new);
// Query UseCases (reference data loading)
i.addLazySingleton(GetVendorsUseCase.new);
i.addLazySingleton(GetRolesByVendorUseCase.new);
i.addLazySingleton(GetHubsUseCase.new);
i.addLazySingleton(GetManagersByHubUseCase.new);
// BLoCs
i.add<RapidOrderBloc>(
() => RapidOrderBloc(
@@ -63,15 +73,36 @@ class ClientCreateOrderModule extends Module {
i.get<AudioRecorderService>(),
),
);
i.add<OneTimeOrderBloc>(OneTimeOrderBloc.new);
i.add<OneTimeOrderBloc>(
() => OneTimeOrderBloc(
i.get<CreateOneTimeOrderUseCase>(),
i.get<GetOrderDetailsForReorderUseCase>(),
i.get<GetVendorsUseCase>(),
i.get<GetRolesByVendorUseCase>(),
i.get<GetHubsUseCase>(),
i.get<GetManagersByHubUseCase>(),
),
);
i.add<PermanentOrderBloc>(
() => PermanentOrderBloc(
i.get<CreatePermanentOrderUseCase>(),
i.get<GetOrderDetailsForReorderUseCase>(),
i.get<ClientOrderQueryRepositoryInterface>(),
i.get<GetVendorsUseCase>(),
i.get<GetRolesByVendorUseCase>(),
i.get<GetHubsUseCase>(),
i.get<GetManagersByHubUseCase>(),
),
);
i.add<RecurringOrderBloc>(
() => RecurringOrderBloc(
i.get<CreateRecurringOrderUseCase>(),
i.get<GetOrderDetailsForReorderUseCase>(),
i.get<GetVendorsUseCase>(),
i.get<GetRolesByVendorUseCase>(),
i.get<GetHubsUseCase>(),
i.get<GetManagersByHubUseCase>(),
),
);
i.add<RecurringOrderBloc>(RecurringOrderBloc.new);
}
@override

View File

@@ -1,15 +1,69 @@
import 'package:krow_core/core.dart';
/// Arguments for the [CreateOneTimeOrderUseCase].
///
/// Wraps the V2 API payload map for a one-time order.
class OneTimeOrderArguments extends UseCaseArgument {
/// Creates a [OneTimeOrderArguments] with the given [payload].
const OneTimeOrderArguments({required this.payload});
/// A single position entry for a one-time order submission.
class OneTimeOrderPositionArgument extends UseCaseArgument {
/// Creates a [OneTimeOrderPositionArgument].
const OneTimeOrderPositionArgument({
required this.roleId,
required this.workerCount,
required this.startTime,
required this.endTime,
this.roleName,
this.lunchBreak,
});
/// The V2 API payload map.
final Map<String, dynamic> payload;
/// The role ID for this position.
final String roleId;
/// Human-readable role name, if available.
final String? roleName;
/// Number of workers needed for this position.
final int workerCount;
/// Shift start time in HH:mm format.
final String startTime;
/// Shift end time in HH:mm format.
final String endTime;
/// Break duration label (e.g. `'MIN_30'`, `'NO_BREAK'`), if set.
final String? lunchBreak;
@override
List<Object?> get props => <Object?>[payload];
List<Object?> get props =>
<Object?>[roleId, roleName, workerCount, startTime, endTime, lunchBreak];
}
/// Typed arguments for [CreateOneTimeOrderUseCase].
///
/// Carries structured form data so the use case can build the V2 API payload.
class OneTimeOrderArguments extends UseCaseArgument {
/// Creates a [OneTimeOrderArguments] with the given structured fields.
const OneTimeOrderArguments({
required this.hubId,
required this.eventName,
required this.orderDate,
required this.positions,
this.vendorId,
});
/// The selected hub ID.
final String hubId;
/// The order event name / title.
final String eventName;
/// The order date.
final DateTime orderDate;
/// The list of position entries.
final List<OneTimeOrderPositionArgument> positions;
/// The selected vendor ID, if applicable.
final String? vendorId;
@override
List<Object?> get props =>
<Object?>[hubId, eventName, orderDate, positions, vendorId];
}

View File

@@ -1,10 +1,75 @@
/// Arguments for the [CreatePermanentOrderUseCase].
///
/// Wraps the V2 API payload map for a permanent order.
class PermanentOrderArguments {
/// Creates a [PermanentOrderArguments] with the given [payload].
const PermanentOrderArguments({required this.payload});
import 'package:krow_core/core.dart';
/// The V2 API payload map.
final Map<String, dynamic> payload;
/// A single position entry for a permanent order submission.
class PermanentOrderPositionArgument extends UseCaseArgument {
/// Creates a [PermanentOrderPositionArgument].
const PermanentOrderPositionArgument({
required this.roleId,
required this.workerCount,
required this.startTime,
required this.endTime,
this.roleName,
});
/// The role ID for this position.
final String roleId;
/// Human-readable role name, if available.
final String? roleName;
/// Number of workers needed for this position.
final int workerCount;
/// Shift start time in HH:mm format.
final String startTime;
/// Shift end time in HH:mm format.
final String endTime;
@override
List<Object?> get props =>
<Object?>[roleId, roleName, workerCount, startTime, endTime];
}
/// Typed arguments for [CreatePermanentOrderUseCase].
///
/// Carries structured form data so the use case can build the V2 API payload.
class PermanentOrderArguments extends UseCaseArgument {
/// Creates a [PermanentOrderArguments] with the given structured fields.
const PermanentOrderArguments({
required this.hubId,
required this.eventName,
required this.startDate,
required this.daysOfWeek,
required this.positions,
this.vendorId,
});
/// The selected hub ID.
final String hubId;
/// The order event name / title.
final String eventName;
/// The start date of the permanent order.
final DateTime startDate;
/// Day-of-week labels (e.g. `['MON', 'WED', 'FRI']`).
final List<String> daysOfWeek;
/// The list of position entries.
final List<PermanentOrderPositionArgument> positions;
/// The selected vendor ID, if applicable.
final String? vendorId;
@override
List<Object?> get props => <Object?>[
hubId,
eventName,
startDate,
daysOfWeek,
positions,
vendorId,
];
}

View File

@@ -1,10 +1,80 @@
/// Arguments for the [CreateRecurringOrderUseCase].
///
/// Wraps the V2 API payload map for a recurring order.
class RecurringOrderArguments {
/// Creates a [RecurringOrderArguments] with the given [payload].
const RecurringOrderArguments({required this.payload});
import 'package:krow_core/core.dart';
/// The V2 API payload map.
final Map<String, dynamic> payload;
/// A single position entry for a recurring order submission.
class RecurringOrderPositionArgument extends UseCaseArgument {
/// Creates a [RecurringOrderPositionArgument].
const RecurringOrderPositionArgument({
required this.roleId,
required this.workerCount,
required this.startTime,
required this.endTime,
this.roleName,
});
/// The role ID for this position.
final String roleId;
/// Human-readable role name, if available.
final String? roleName;
/// Number of workers needed for this position.
final int workerCount;
/// Shift start time in HH:mm format.
final String startTime;
/// Shift end time in HH:mm format.
final String endTime;
@override
List<Object?> get props =>
<Object?>[roleId, roleName, workerCount, startTime, endTime];
}
/// Typed arguments for [CreateRecurringOrderUseCase].
///
/// Carries structured form data so the use case can build the V2 API payload.
class RecurringOrderArguments extends UseCaseArgument {
/// Creates a [RecurringOrderArguments] with the given structured fields.
const RecurringOrderArguments({
required this.hubId,
required this.eventName,
required this.startDate,
required this.endDate,
required this.recurringDays,
required this.positions,
this.vendorId,
});
/// The selected hub ID.
final String hubId;
/// The order event name / title.
final String eventName;
/// The start date of the recurring order period.
final DateTime startDate;
/// The end date of the recurring order period.
final DateTime endDate;
/// Day-of-week labels (e.g. `['MON', 'WED', 'FRI']`).
final List<String> recurringDays;
/// The list of position entries.
final List<RecurringOrderPositionArgument> positions;
/// The selected vendor ID, if applicable.
final String? vendorId;
@override
List<Object?> get props => <Object?>[
hubId,
eventName,
startDate,
endDate,
recurringDays,
positions,
vendorId,
];
}

View File

@@ -5,16 +5,45 @@ import '../repositories/client_create_order_repository_interface.dart';
/// Use case for creating a one-time staffing order.
///
/// Delegates the V2 API payload to the repository.
/// Builds the V2 API payload from typed [OneTimeOrderArguments] and
/// delegates submission to the repository. Payload construction (date
/// formatting, position mapping, break-minutes conversion) is business
/// logic that belongs here, not in the BLoC.
class CreateOneTimeOrderUseCase
implements UseCase<OneTimeOrderArguments, void> {
/// Creates a [CreateOneTimeOrderUseCase].
const CreateOneTimeOrderUseCase(this._repository);
/// The create-order repository.
final ClientCreateOrderRepositoryInterface _repository;
@override
Future<void> call(OneTimeOrderArguments input) {
return _repository.createOneTimeOrder(input.payload);
final String orderDate = formatDateToIso(input.orderDate);
final List<Map<String, dynamic>> positions =
input.positions.map((OneTimeOrderPositionArgument p) {
return <String, dynamic>{
if (p.roleName != null) 'roleName': p.roleName,
if (p.roleId.isNotEmpty) 'roleId': p.roleId,
'workerCount': p.workerCount,
'startTime': p.startTime,
'endTime': p.endTime,
if (p.lunchBreak != null &&
p.lunchBreak != 'NO_BREAK' &&
p.lunchBreak!.isNotEmpty)
'lunchBreakMinutes': breakMinutesFromLabel(p.lunchBreak!),
};
}).toList();
final Map<String, dynamic> payload = <String, dynamic>{
'hubId': input.hubId,
'eventName': input.eventName,
'orderDate': orderDate,
'positions': positions,
if (input.vendorId != null) 'vendorId': input.vendorId,
};
return _repository.createOneTimeOrder(payload);
}
}

View File

@@ -1,17 +1,61 @@
import 'package:krow_core/core.dart';
import '../arguments/permanent_order_arguments.dart';
import '../repositories/client_create_order_repository_interface.dart';
/// Day-of-week labels in Sunday-first order, matching the V2 API convention.
const List<String> _dayLabels = <String>[
'SUN',
'MON',
'TUE',
'WED',
'THU',
'FRI',
'SAT',
];
/// Use case for creating a permanent staffing order.
///
/// Delegates the V2 API payload to the repository.
class CreatePermanentOrderUseCase {
/// Builds the V2 API payload from typed [PermanentOrderArguments] and
/// delegates submission to the repository. Payload construction (date
/// formatting, day-of-week mapping, position mapping) is business
/// logic that belongs here, not in the BLoC.
class CreatePermanentOrderUseCase
implements UseCase<PermanentOrderArguments, void> {
/// Creates a [CreatePermanentOrderUseCase].
const CreatePermanentOrderUseCase(this._repository);
/// The create-order repository.
final ClientCreateOrderRepositoryInterface _repository;
/// Executes the use case with the given [args].
Future<void> call(PermanentOrderArguments args) {
return _repository.createPermanentOrder(args.payload);
@override
Future<void> call(PermanentOrderArguments input) {
final String startDate = formatDateToIso(input.startDate);
final List<int> daysOfWeek = input.daysOfWeek
.map((String day) => _dayLabels.indexOf(day) % 7)
.toList();
final List<Map<String, dynamic>> positions =
input.positions.map((PermanentOrderPositionArgument p) {
return <String, dynamic>{
if (p.roleName != null) 'roleName': p.roleName,
if (p.roleId.isNotEmpty) 'roleId': p.roleId,
'workerCount': p.workerCount,
'startTime': p.startTime,
'endTime': p.endTime,
};
}).toList();
final Map<String, dynamic> payload = <String, dynamic>{
'hubId': input.hubId,
'eventName': input.eventName,
'startDate': startDate,
'daysOfWeek': daysOfWeek,
'positions': positions,
if (input.vendorId != null) 'vendorId': input.vendorId,
};
return _repository.createPermanentOrder(payload);
}
}

View File

@@ -1,17 +1,63 @@
import 'package:krow_core/core.dart';
import '../arguments/recurring_order_arguments.dart';
import '../repositories/client_create_order_repository_interface.dart';
/// Day-of-week labels in Sunday-first order, matching the V2 API convention.
const List<String> _dayLabels = <String>[
'SUN',
'MON',
'TUE',
'WED',
'THU',
'FRI',
'SAT',
];
/// Use case for creating a recurring staffing order.
///
/// Delegates the V2 API payload to the repository.
class CreateRecurringOrderUseCase {
/// Builds the V2 API payload from typed [RecurringOrderArguments] and
/// delegates submission to the repository. Payload construction (date
/// formatting, recurrence-day mapping, position mapping) is business
/// logic that belongs here, not in the BLoC.
class CreateRecurringOrderUseCase
implements UseCase<RecurringOrderArguments, void> {
/// Creates a [CreateRecurringOrderUseCase].
const CreateRecurringOrderUseCase(this._repository);
/// The create-order repository.
final ClientCreateOrderRepositoryInterface _repository;
/// Executes the use case with the given [args].
Future<void> call(RecurringOrderArguments args) {
return _repository.createRecurringOrder(args.payload);
@override
Future<void> call(RecurringOrderArguments input) {
final String startDate = formatDateToIso(input.startDate);
final String endDate = formatDateToIso(input.endDate);
final List<int> recurrenceDays = input.recurringDays
.map((String day) => _dayLabels.indexOf(day) % 7)
.toList();
final List<Map<String, dynamic>> positions =
input.positions.map((RecurringOrderPositionArgument p) {
return <String, dynamic>{
if (p.roleName != null) 'roleName': p.roleName,
if (p.roleId.isNotEmpty) 'roleId': p.roleId,
'workerCount': p.workerCount,
'startTime': p.startTime,
'endTime': p.endTime,
};
}).toList();
final Map<String, dynamic> payload = <String, dynamic>{
'hubId': input.hubId,
'eventName': input.eventName,
'startDate': startDate,
'endDate': endDate,
'recurrenceDays': recurrenceDays,
'positions': positions,
if (input.vendorId != null) 'vendorId': input.vendorId,
};
return _repository.createRecurringOrder(payload);
}
}

View File

@@ -0,0 +1,20 @@
import 'package:krow_core/core.dart';
import '../models/order_hub.dart';
import '../repositories/client_order_query_repository_interface.dart';
/// Use case for fetching team hubs for the current business.
///
/// Returns the list of [OrderHub] instances available for order assignment.
class GetHubsUseCase implements NoInputUseCase<List<OrderHub>> {
/// Creates a [GetHubsUseCase].
const GetHubsUseCase(this._repository);
/// The query repository for order reference data.
final ClientOrderQueryRepositoryInterface _repository;
@override
Future<List<OrderHub>> call() {
return _repository.getHubs();
}
}

View File

@@ -0,0 +1,21 @@
import 'package:krow_core/core.dart';
import '../models/order_manager.dart';
import '../repositories/client_order_query_repository_interface.dart';
/// Use case for fetching managers assigned to a specific hub.
///
/// Takes a hub ID and returns the list of [OrderManager] instances
/// for that hub.
class GetManagersByHubUseCase implements UseCase<String, List<OrderManager>> {
/// Creates a [GetManagersByHubUseCase].
const GetManagersByHubUseCase(this._repository);
/// The query repository for order reference data.
final ClientOrderQueryRepositoryInterface _repository;
@override
Future<List<OrderManager>> call(String hubId) {
return _repository.getManagersByHub(hubId);
}
}

View File

@@ -0,0 +1,21 @@
import 'package:krow_core/core.dart';
import '../models/order_role.dart';
import '../repositories/client_order_query_repository_interface.dart';
/// Use case for fetching roles offered by a specific vendor.
///
/// Takes a vendor ID and returns the list of [OrderRole] instances
/// available from that vendor.
class GetRolesByVendorUseCase implements UseCase<String, List<OrderRole>> {
/// Creates a [GetRolesByVendorUseCase].
const GetRolesByVendorUseCase(this._repository);
/// The query repository for order reference data.
final ClientOrderQueryRepositoryInterface _repository;
@override
Future<List<OrderRole>> call(String vendorId) {
return _repository.getRolesByVendor(vendorId);
}
}

View File

@@ -0,0 +1,21 @@
import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart';
import '../repositories/client_order_query_repository_interface.dart';
/// Use case for fetching the list of available vendors.
///
/// Wraps the query repository to enforce the use-case boundary between
/// presentation and data layers.
class GetVendorsUseCase implements NoInputUseCase<List<Vendor>> {
/// Creates a [GetVendorsUseCase].
const GetVendorsUseCase(this._repository);
/// The query repository for order reference data.
final ClientOrderQueryRepositoryInterface _repository;
@override
Future<List<Vendor>> call() {
return _repository.getVendors();
}
}

View File

@@ -2,9 +2,12 @@ import 'package:client_create_order/src/domain/arguments/one_time_order_argument
import 'package:client_create_order/src/domain/models/order_hub.dart';
import 'package:client_create_order/src/domain/models/order_manager.dart';
import 'package:client_create_order/src/domain/models/order_role.dart';
import 'package:client_create_order/src/domain/repositories/client_order_query_repository_interface.dart';
import 'package:client_create_order/src/domain/usecases/create_one_time_order_usecase.dart';
import 'package:client_create_order/src/domain/usecases/get_hubs_usecase.dart';
import 'package:client_create_order/src/domain/usecases/get_managers_by_hub_usecase.dart';
import 'package:client_create_order/src/domain/usecases/get_order_details_for_reorder_usecase.dart';
import 'package:client_create_order/src/domain/usecases/get_roles_by_vendor_usecase.dart';
import 'package:client_create_order/src/domain/usecases/get_vendors_usecase.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart';
@@ -14,16 +17,20 @@ import 'one_time_order_state.dart';
/// BLoC for managing the multi-step one-time order creation form.
///
/// Builds V2 API payloads and uses [OrderPreview] for reorder.
/// Delegates all data fetching to query use cases and order submission
/// to [CreateOneTimeOrderUseCase]. Uses [OrderPreview] for reorder.
class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
with
BlocErrorHandler<OneTimeOrderState>,
SafeBloc<OneTimeOrderEvent, OneTimeOrderState> {
/// Creates the BLoC with required dependencies.
/// Creates the BLoC with required use case dependencies.
OneTimeOrderBloc(
this._createOneTimeOrderUseCase,
this._getOrderDetailsForReorderUseCase,
this._queryRepository,
this._getVendorsUseCase,
this._getRolesByVendorUseCase,
this._getHubsUseCase,
this._getManagersByHubUseCase,
) : super(OneTimeOrderState.initial()) {
on<OneTimeOrderVendorsLoaded>(_onVendorsLoaded);
on<OneTimeOrderVendorChanged>(_onVendorChanged);
@@ -45,16 +52,21 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
final CreateOneTimeOrderUseCase _createOneTimeOrderUseCase;
final GetOrderDetailsForReorderUseCase _getOrderDetailsForReorderUseCase;
final ClientOrderQueryRepositoryInterface _queryRepository;
final GetVendorsUseCase _getVendorsUseCase;
final GetRolesByVendorUseCase _getRolesByVendorUseCase;
final GetHubsUseCase _getHubsUseCase;
final GetManagersByHubUseCase _getManagersByHubUseCase;
/// Loads available vendors via the use case.
Future<void> _loadVendors() async {
final List<Vendor>? vendors = await handleErrorWithResult(
action: () => _queryRepository.getVendors(),
action: () => _getVendorsUseCase(),
onError: (_) => add(const OneTimeOrderVendorsLoaded(<Vendor>[])),
);
if (vendors != null) add(OneTimeOrderVendorsLoaded(vendors));
}
/// Loads roles for [vendorId] and maps them to presentation option models.
Future<void> _loadRolesForVendor(
String vendorId,
Emitter<OneTimeOrderState> emit,
@@ -62,7 +74,7 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
final List<OneTimeOrderRoleOption>? roles = await handleErrorWithResult(
action: () async {
final List<OrderRole> result =
await _queryRepository.getRolesByVendor(vendorId);
await _getRolesByVendorUseCase(vendorId);
return result
.map((OrderRole r) => OneTimeOrderRoleOption(
id: r.id, name: r.name, costPerHour: r.costPerHour))
@@ -74,10 +86,11 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
if (roles != null) emit(state.copyWith(roles: roles));
}
/// Loads hubs via the use case and maps to presentation option models.
Future<void> _loadHubs() async {
final List<OneTimeOrderHubOption>? hubs = await handleErrorWithResult(
action: () async {
final List<OrderHub> result = await _queryRepository.getHubs();
final List<OrderHub> result = await _getHubsUseCase();
return result
.map((OrderHub h) => OneTimeOrderHubOption(
id: h.id,
@@ -100,12 +113,13 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
if (hubs != null) add(OneTimeOrderHubsLoaded(hubs));
}
/// Loads managers for [hubId] via the use case.
Future<void> _loadManagersForHub(String hubId) async {
final List<OneTimeOrderManagerOption>? managers =
await handleErrorWithResult(
action: () async {
final List<OrderManager> result =
await _queryRepository.getManagersByHub(hubId);
await _getManagersByHubUseCase(hubId);
return result
.map((OrderManager m) =>
OneTimeOrderManagerOption(id: m.id, name: m.name))
@@ -224,7 +238,7 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
emit(state.copyWith(positions: newPositions));
}
/// Builds a V2 API payload and submits the one-time order.
/// Builds typed arguments from form state and submits via the use case.
Future<void> _onSubmitted(
OneTimeOrderSubmitted event,
Emitter<OneTimeOrderState> emit,
@@ -236,12 +250,7 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
final OneTimeOrderHubOption? selectedHub = state.selectedHub;
if (selectedHub == null) throw const OrderMissingHubException();
final String orderDate =
'${state.date.year.toString().padLeft(4, '0')}-'
'${state.date.month.toString().padLeft(2, '0')}-'
'${state.date.day.toString().padLeft(2, '0')}';
final List<Map<String, dynamic>> positions =
final List<OneTimeOrderPositionArgument> positionArgs =
state.positions.map((OneTimeOrderPosition p) {
final OneTimeOrderRoleOption? role = state.roles
.cast<OneTimeOrderRoleOption?>()
@@ -249,28 +258,24 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
(OneTimeOrderRoleOption? r) => r != null && r.id == p.role,
orElse: () => null,
);
return <String, dynamic>{
if (role != null) 'roleName': role.name,
if (p.role.isNotEmpty) 'roleId': p.role,
'workerCount': p.count,
'startTime': p.startTime,
'endTime': p.endTime,
if (p.lunchBreak != 'NO_BREAK' && p.lunchBreak.isNotEmpty)
'lunchBreakMinutes': _breakMinutes(p.lunchBreak),
};
return OneTimeOrderPositionArgument(
roleId: p.role,
roleName: role?.name,
workerCount: p.count,
startTime: p.startTime,
endTime: p.endTime,
lunchBreak: p.lunchBreak,
);
}).toList();
final Map<String, dynamic> payload = <String, dynamic>{
'hubId': selectedHub.id,
'eventName': state.eventName,
'orderDate': orderDate,
'positions': positions,
if (state.selectedVendor != null)
'vendorId': state.selectedVendor!.id,
};
await _createOneTimeOrderUseCase(
OneTimeOrderArguments(payload: payload),
OneTimeOrderArguments(
hubId: selectedHub.id,
eventName: state.eventName,
orderDate: state.date,
positions: positionArgs,
vendorId: state.selectedVendor?.id,
),
);
emit(state.copyWith(status: OneTimeOrderStatus.success));
},
@@ -339,8 +344,8 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
positions.add(OneTimeOrderPosition(
role: role.roleId,
count: role.workersNeeded,
startTime: _formatTime(shift.startsAt),
endTime: _formatTime(shift.endsAt),
startTime: formatTimeHHmm(shift.startsAt),
endTime: formatTimeHHmm(shift.endsAt),
));
}
}
@@ -357,29 +362,4 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
),
);
}
/// Formats a [DateTime] to HH:mm string.
String _formatTime(DateTime dt) {
final DateTime local = dt.toLocal();
return '${local.hour.toString().padLeft(2, '0')}:'
'${local.minute.toString().padLeft(2, '0')}';
}
/// Converts a break duration string to minutes.
int _breakMinutes(String value) {
switch (value) {
case 'MIN_10':
return 10;
case 'MIN_15':
return 15;
case 'MIN_30':
return 30;
case 'MIN_45':
return 45;
case 'MIN_60':
return 60;
default:
return 0;
}
}
}

View File

@@ -1,26 +1,36 @@
import 'package:client_create_order/src/domain/arguments/permanent_order_arguments.dart';
import 'package:client_create_order/src/domain/models/order_hub.dart';
import 'package:client_create_order/src/domain/models/order_manager.dart';
import 'package:client_create_order/src/domain/models/order_role.dart';
import 'package:client_create_order/src/domain/repositories/client_order_query_repository_interface.dart';
import 'package:client_create_order/src/domain/usecases/create_permanent_order_usecase.dart';
import 'package:client_create_order/src/domain/usecases/get_hubs_usecase.dart';
import 'package:client_create_order/src/domain/usecases/get_managers_by_hub_usecase.dart';
import 'package:client_create_order/src/domain/usecases/get_order_details_for_reorder_usecase.dart';
import 'package:client_create_order/src/domain/usecases/get_roles_by_vendor_usecase.dart';
import 'package:client_create_order/src/domain/usecases/get_vendors_usecase.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow_core/core.dart';
import 'package:client_create_order/src/domain/arguments/permanent_order_arguments.dart';
import 'package:krow_domain/krow_domain.dart' as domain;
import 'permanent_order_event.dart';
import 'permanent_order_state.dart';
/// BLoC for managing the permanent order creation form.
///
/// Delegates all data fetching to query use cases and order submission
/// to [CreatePermanentOrderUseCase].
class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
with
BlocErrorHandler<PermanentOrderState>,
SafeBloc<PermanentOrderEvent, PermanentOrderState> {
/// Creates a BLoC with required use case dependencies.
PermanentOrderBloc(
this._createPermanentOrderUseCase,
this._getOrderDetailsForReorderUseCase,
this._queryRepository,
this._getVendorsUseCase,
this._getRolesByVendorUseCase,
this._getHubsUseCase,
this._getManagersByHubUseCase,
) : super(PermanentOrderState.initial()) {
on<PermanentOrderVendorsLoaded>(_onVendorsLoaded);
on<PermanentOrderVendorChanged>(_onVendorChanged);
@@ -43,7 +53,10 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
final CreatePermanentOrderUseCase _createPermanentOrderUseCase;
final GetOrderDetailsForReorderUseCase _getOrderDetailsForReorderUseCase;
final ClientOrderQueryRepositoryInterface _queryRepository;
final GetVendorsUseCase _getVendorsUseCase;
final GetRolesByVendorUseCase _getRolesByVendorUseCase;
final GetHubsUseCase _getHubsUseCase;
final GetManagersByHubUseCase _getManagersByHubUseCase;
static const List<String> _dayLabels = <String>[
'SUN',
@@ -55,9 +68,10 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
'SAT',
];
/// Loads available vendors via the use case.
Future<void> _loadVendors() async {
final List<domain.Vendor>? vendors = await handleErrorWithResult(
action: () => _queryRepository.getVendors(),
action: () => _getVendorsUseCase(),
onError: (_) => add(const PermanentOrderVendorsLoaded(<domain.Vendor>[])),
);
@@ -66,6 +80,8 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
}
}
/// Loads roles for [vendorId] via the use case and maps them to
/// presentation option models.
Future<void> _loadRolesForVendor(
String vendorId,
Emitter<PermanentOrderState> emit,
@@ -73,7 +89,7 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
final List<PermanentOrderRoleOption>? roles = await handleErrorWithResult(
action: () async {
final List<OrderRole> orderRoles =
await _queryRepository.getRolesByVendor(vendorId);
await _getRolesByVendorUseCase(vendorId);
return orderRoles
.map(
(OrderRole r) => PermanentOrderRoleOption(
@@ -93,10 +109,11 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
}
}
/// Loads hubs via the use case and maps them to presentation option models.
Future<void> _loadHubs() async {
final List<PermanentOrderHubOption>? hubs = await handleErrorWithResult(
action: () async {
final List<OrderHub> orderHubs = await _queryRepository.getHubs();
final List<OrderHub> orderHubs = await _getHubsUseCase();
return orderHubs
.map(
(OrderHub hub) => PermanentOrderHubOption(
@@ -193,6 +210,7 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
emit(state.copyWith(managers: event.managers));
}
/// Loads managers for [hubId] via the use case.
Future<void> _loadManagersForHub(
String hubId,
Emitter<PermanentOrderState> emit,
@@ -201,7 +219,7 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
await handleErrorWithResult(
action: () async {
final List<OrderManager> orderManagers =
await _queryRepository.getManagersByHub(hubId);
await _getManagersByHubUseCase(hubId);
return orderManagers
.map(
(OrderManager m) => PermanentOrderManagerOption(
@@ -221,7 +239,6 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
}
}
void _onEventNameChanged(
PermanentOrderEventNameChanged event,
Emitter<PermanentOrderState> emit,
@@ -315,6 +332,7 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
emit(state.copyWith(positions: newPositions));
}
/// Builds typed arguments from form state and submits via the use case.
Future<void> _onSubmitted(
PermanentOrderSubmitted event,
Emitter<PermanentOrderState> emit,
@@ -328,16 +346,7 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
throw const domain.OrderMissingHubException();
}
final String startDate =
'${state.startDate.year.toString().padLeft(4, '0')}-'
'${state.startDate.month.toString().padLeft(2, '0')}-'
'${state.startDate.day.toString().padLeft(2, '0')}';
final List<int> daysOfWeek = state.permanentDays
.map((String day) => _dayLabels.indexOf(day) % 7)
.toList();
final List<Map<String, dynamic>> positions =
final List<PermanentOrderPositionArgument> positionArgs =
state.positions.map((PermanentOrderPosition p) {
final PermanentOrderRoleOption? role = state.roles
.cast<PermanentOrderRoleOption?>()
@@ -345,27 +354,24 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
(PermanentOrderRoleOption? r) => r != null && r.id == p.role,
orElse: () => null,
);
return <String, dynamic>{
if (role != null) 'roleName': role.name,
if (p.role.isNotEmpty) 'roleId': p.role,
'workerCount': p.count,
'startTime': p.startTime,
'endTime': p.endTime,
};
return PermanentOrderPositionArgument(
roleId: p.role,
roleName: role?.name,
workerCount: p.count,
startTime: p.startTime,
endTime: p.endTime,
);
}).toList();
final Map<String, dynamic> payload = <String, dynamic>{
'hubId': selectedHub.id,
'eventName': state.eventName,
'startDate': startDate,
'daysOfWeek': daysOfWeek,
'positions': positions,
if (state.selectedVendor != null)
'vendorId': state.selectedVendor!.id,
};
await _createPermanentOrderUseCase(
PermanentOrderArguments(payload: payload),
PermanentOrderArguments(
hubId: selectedHub.id,
eventName: state.eventName,
startDate: state.startDate,
daysOfWeek: state.permanentDays,
positions: positionArgs,
vendorId: state.selectedVendor?.id,
),
);
emit(state.copyWith(status: PermanentOrderStatus.success));
},
@@ -376,6 +382,7 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
);
}
/// Initializes the form from route arguments or reorder preview data.
Future<void> _onInitialized(
PermanentOrderInitialized event,
Emitter<PermanentOrderState> emit,
@@ -406,8 +413,8 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
positions.add(PermanentOrderPosition(
role: role.roleId,
count: role.workersNeeded,
startTime: _formatTime(shift.startsAt),
endTime: _formatTime(shift.endsAt),
startTime: formatTimeHHmm(shift.startsAt),
endTime: formatTimeHHmm(shift.endsAt),
));
}
}
@@ -430,13 +437,6 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
);
}
/// Formats a [DateTime] to HH:mm string.
String _formatTime(DateTime dt) {
final DateTime local = dt.toLocal();
return '${local.hour.toString().padLeft(2, '0')}:'
'${local.minute.toString().padLeft(2, '0')}';
}
static List<String> _sortDays(List<String> days) {
days.sort(
(String a, String b) =>

View File

@@ -1,12 +1,15 @@
import 'package:client_create_order/src/domain/arguments/recurring_order_arguments.dart';
import 'package:client_create_order/src/domain/models/order_hub.dart';
import 'package:client_create_order/src/domain/models/order_manager.dart';
import 'package:client_create_order/src/domain/models/order_role.dart';
import 'package:client_create_order/src/domain/repositories/client_order_query_repository_interface.dart';
import 'package:client_create_order/src/domain/usecases/create_recurring_order_usecase.dart';
import 'package:client_create_order/src/domain/usecases/get_hubs_usecase.dart';
import 'package:client_create_order/src/domain/usecases/get_managers_by_hub_usecase.dart';
import 'package:client_create_order/src/domain/usecases/get_order_details_for_reorder_usecase.dart';
import 'package:client_create_order/src/domain/usecases/get_roles_by_vendor_usecase.dart';
import 'package:client_create_order/src/domain/usecases/get_vendors_usecase.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow_core/core.dart';
import 'package:client_create_order/src/domain/arguments/recurring_order_arguments.dart';
import 'package:krow_domain/krow_domain.dart' as domain;
import 'recurring_order_event.dart';
@@ -14,19 +17,20 @@ import 'recurring_order_state.dart';
/// BLoC for managing the recurring order creation form.
///
/// Delegates all backend queries to [ClientOrderQueryRepositoryInterface]
/// and order submission to [CreateRecurringOrderUseCase].
/// Builds V2 API payloads from form state.
/// Delegates all data fetching to query use cases and order submission
/// to [CreateRecurringOrderUseCase]. Builds V2 API payloads from form state.
class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
with
BlocErrorHandler<RecurringOrderState>,
SafeBloc<RecurringOrderEvent, RecurringOrderState> {
/// Creates a [RecurringOrderBloc] with the required use cases and
/// query repository.
/// Creates a [RecurringOrderBloc] with the required use case dependencies.
RecurringOrderBloc(
this._createRecurringOrderUseCase,
this._getOrderDetailsForReorderUseCase,
this._queryRepository,
this._getVendorsUseCase,
this._getRolesByVendorUseCase,
this._getHubsUseCase,
this._getManagersByHubUseCase,
) : super(RecurringOrderState.initial()) {
on<RecurringOrderVendorsLoaded>(_onVendorsLoaded);
on<RecurringOrderVendorChanged>(_onVendorChanged);
@@ -50,7 +54,10 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
final CreateRecurringOrderUseCase _createRecurringOrderUseCase;
final GetOrderDetailsForReorderUseCase _getOrderDetailsForReorderUseCase;
final ClientOrderQueryRepositoryInterface _queryRepository;
final GetVendorsUseCase _getVendorsUseCase;
final GetRolesByVendorUseCase _getRolesByVendorUseCase;
final GetHubsUseCase _getHubsUseCase;
final GetManagersByHubUseCase _getManagersByHubUseCase;
static const List<String> _dayLabels = <String>[
'SUN',
@@ -62,12 +69,10 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
'SAT',
];
/// Loads the list of available vendors from the query repository.
/// Loads the list of available vendors via the use case.
Future<void> _loadVendors() async {
final List<domain.Vendor>? vendors = await handleErrorWithResult(
action: () async {
return _queryRepository.getVendors();
},
action: () => _getVendorsUseCase(),
onError: (_) =>
add(const RecurringOrderVendorsLoaded(<domain.Vendor>[])),
);
@@ -77,8 +82,8 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
}
}
/// Loads roles for the given [vendorId] and maps them to presentation
/// option models.
/// Loads roles for [vendorId] via the use case and maps them to
/// presentation option models.
Future<void> _loadRolesForVendor(
String vendorId,
Emitter<RecurringOrderState> emit,
@@ -86,7 +91,7 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
final List<RecurringOrderRoleOption>? roles = await handleErrorWithResult(
action: () async {
final List<OrderRole> orderRoles =
await _queryRepository.getRolesByVendor(vendorId);
await _getRolesByVendorUseCase(vendorId);
return orderRoles
.map(
(OrderRole r) => RecurringOrderRoleOption(
@@ -106,12 +111,12 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
}
}
/// Loads team hubs for the current business owner and maps them to
/// presentation option models.
/// Loads team hubs via the use case and maps them to presentation
/// option models.
Future<void> _loadHubs() async {
final List<RecurringOrderHubOption>? hubs = await handleErrorWithResult(
action: () async {
final List<OrderHub> orderHubs = await _queryRepository.getHubs();
final List<OrderHub> orderHubs = await _getHubsUseCase();
return orderHubs
.map(
(OrderHub hub) => RecurringOrderHubOption(
@@ -208,8 +213,8 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
emit(state.copyWith(managers: event.managers));
}
/// Loads managers for the given [hubId] and maps them to presentation
/// option models.
/// Loads managers for [hubId] via the use case and maps them to
/// presentation option models.
Future<void> _loadManagersForHub(
String hubId,
Emitter<RecurringOrderState> emit,
@@ -218,7 +223,7 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
await handleErrorWithResult(
action: () async {
final List<OrderManager> orderManagers =
await _queryRepository.getManagersByHub(hubId);
await _getManagersByHubUseCase(hubId);
return orderManagers
.map(
(OrderManager m) => RecurringOrderManagerOption(
@@ -347,6 +352,7 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
emit(state.copyWith(positions: newPositions));
}
/// Builds typed arguments from form state and submits via the use case.
Future<void> _onSubmitted(
RecurringOrderSubmitted event,
Emitter<RecurringOrderState> emit,
@@ -360,21 +366,7 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
throw const domain.OrderMissingHubException();
}
final String startDate =
'${state.startDate.year.toString().padLeft(4, '0')}-'
'${state.startDate.month.toString().padLeft(2, '0')}-'
'${state.startDate.day.toString().padLeft(2, '0')}';
final String endDate =
'${state.endDate.year.toString().padLeft(4, '0')}-'
'${state.endDate.month.toString().padLeft(2, '0')}-'
'${state.endDate.day.toString().padLeft(2, '0')}';
// Map day labels (MON=1, TUE=2, ..., SUN=0) to V2 int format
final List<int> recurrenceDays = state.recurringDays
.map((String day) => _dayLabels.indexOf(day) % 7)
.toList();
final List<Map<String, dynamic>> positions =
final List<RecurringOrderPositionArgument> positionArgs =
state.positions.map((RecurringOrderPosition p) {
final RecurringOrderRoleOption? role = state.roles
.cast<RecurringOrderRoleOption?>()
@@ -382,28 +374,25 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
(RecurringOrderRoleOption? r) => r != null && r.id == p.role,
orElse: () => null,
);
return <String, dynamic>{
if (role != null) 'roleName': role.name,
if (p.role.isNotEmpty) 'roleId': p.role,
'workerCount': p.count,
'startTime': p.startTime,
'endTime': p.endTime,
};
return RecurringOrderPositionArgument(
roleId: p.role,
roleName: role?.name,
workerCount: p.count,
startTime: p.startTime,
endTime: p.endTime,
);
}).toList();
final Map<String, dynamic> payload = <String, dynamic>{
'hubId': selectedHub.id,
'eventName': state.eventName,
'startDate': startDate,
'endDate': endDate,
'recurrenceDays': recurrenceDays,
'positions': positions,
if (state.selectedVendor != null)
'vendorId': state.selectedVendor!.id,
};
await _createRecurringOrderUseCase(
RecurringOrderArguments(payload: payload),
RecurringOrderArguments(
hubId: selectedHub.id,
eventName: state.eventName,
startDate: state.startDate,
endDate: state.endDate,
recurringDays: state.recurringDays,
positions: positionArgs,
vendorId: state.selectedVendor?.id,
),
);
emit(state.copyWith(status: RecurringOrderStatus.success));
},
@@ -414,6 +403,7 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
);
}
/// Initializes the form from route arguments or reorder preview data.
Future<void> _onInitialized(
RecurringOrderInitialized event,
Emitter<RecurringOrderState> emit,
@@ -445,8 +435,8 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
positions.add(RecurringOrderPosition(
role: role.roleId,
count: role.workersNeeded,
startTime: _formatTime(shift.startsAt),
endTime: _formatTime(shift.endsAt),
startTime: formatTimeHHmm(shift.startsAt),
endTime: formatTimeHHmm(shift.endsAt),
));
}
}
@@ -470,13 +460,6 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
);
}
/// Formats a [DateTime] to HH:mm string.
String _formatTime(DateTime dt) {
final DateTime local = dt.toLocal();
return '${local.hour.toString().padLeft(2, '0')}:'
'${local.minute.toString().padLeft(2, '0')}';
}
static List<String> _sortDays(List<String> days) {
days.sort(
(String a, String b) =>

View File

@@ -1,13 +1,13 @@
import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart';
import '../../domain/repositories/i_view_orders_repository.dart';
import '../../domain/repositories/view_orders_repository_interface.dart';
/// V2 API implementation of [IViewOrdersRepository].
/// V2 API implementation of [ViewOrdersRepositoryInterface].
///
/// Replaces the old Data Connect implementation with [BaseApiService] calls
/// to the V2 query and command API endpoints.
class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
class ViewOrdersRepositoryImpl implements ViewOrdersRepositoryInterface {
/// Creates an instance backed by the given [apiService].
ViewOrdersRepositoryImpl({required BaseApiService apiService})
: _api = apiService;

View File

@@ -4,7 +4,7 @@ import 'package:krow_domain/krow_domain.dart';
///
/// V2 API returns workers inline with order items, so the separate
/// accepted-applications method is no longer needed.
abstract class IViewOrdersRepository {
abstract class ViewOrdersRepositoryInterface {
/// Fetches [OrderItem] list for the given date range via the V2 API.
Future<List<OrderItem>> getOrdersForRange({
required DateTime start,

View File

@@ -1,18 +1,18 @@
import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart';
import '../repositories/i_view_orders_repository.dart';
import '../repositories/view_orders_repository_interface.dart';
import '../arguments/orders_range_arguments.dart';
/// Use case for retrieving the list of client orders.
///
/// This use case encapsulates the business rule of fetching orders
/// and delegates the data retrieval to the [IViewOrdersRepository].
/// and delegates the data retrieval to the [ViewOrdersRepositoryInterface].
class GetOrdersUseCase
implements UseCase<OrdersRangeArguments, List<OrderItem>> {
/// Creates a [GetOrdersUseCase] with the required [IViewOrdersRepository].
/// Creates a [GetOrdersUseCase] with the required [ViewOrdersRepositoryInterface].
GetOrdersUseCase(this._repository);
final IViewOrdersRepository _repository;
final ViewOrdersRepositoryInterface _repository;
@override
Future<List<OrderItem>> call(OrdersRangeArguments input) {

View File

@@ -4,11 +4,11 @@ import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_domain/krow_domain.dart';
import '../../domain/repositories/i_view_orders_repository.dart';
import '../../domain/repositories/view_orders_repository_interface.dart';
/// Bottom sheet for editing an existing order via the V2 API.
///
/// Delegates all backend calls through [IViewOrdersRepository].
/// Delegates all backend calls through [ViewOrdersRepositoryInterface].
/// The V2 `clientOrderEdit` endpoint creates an edited copy.
class OrderEditSheet extends StatefulWidget {
/// Creates an [OrderEditSheet] for the given [order].
@@ -39,12 +39,12 @@ class OrderEditSheetState extends State<OrderEditSheet> {
List<Map<String, dynamic>> _hubs = const <Map<String, dynamic>>[];
Map<String, dynamic>? _selectedHub;
late IViewOrdersRepository _repository;
late ViewOrdersRepositoryInterface _repository;
@override
void initState() {
super.initState();
_repository = Modular.get<IViewOrdersRepository>();
_repository = Modular.get<ViewOrdersRepositoryInterface>();
_orderNameController = TextEditingController(text: widget.order.roleName);
final String startHH =
@@ -441,9 +441,9 @@ class OrderEditSheetState extends State<OrderEditSheet> {
const SizedBox(height: UiConstants.space3),
// Role selector
_buildSectionHeader('ROLE'),
_buildSectionHeader('ROLE'), // TODO: localize
_buildDropdown(
hint: 'Select role',
hint: 'Select role', // TODO: localize
value: roleName.isNotEmpty ? roleName : null,
items: _roles
.map((Map<String, dynamic> r) => r['roleName'] as String? ?? r['name'] as String? ?? '')
@@ -495,7 +495,7 @@ class OrderEditSheetState extends State<OrderEditSheet> {
children: <Widget>[
Expanded(
child: _buildInlineTimeInput(
label: 'Start Time',
label: 'Start Time', // TODO: localize
value: pos['startTime'] as String? ?? '09:00',
onTap: () async {
final TimeOfDay? picked = await showTimePicker(
@@ -513,7 +513,7 @@ class OrderEditSheetState extends State<OrderEditSheet> {
const SizedBox(width: UiConstants.space3),
Expanded(
child: _buildInlineTimeInput(
label: 'End Time',
label: 'End Time', // TODO: localize
value: pos['endTime'] as String? ?? '17:00',
onTap: () async {
final TimeOfDay? picked = await showTimePicker(
@@ -825,6 +825,7 @@ class OrderEditSheetState extends State<OrderEditSheet> {
style: UiTypography.body2b.textPrimary,
),
Text(
// TODO: localize
'${pos['workerCount']} worker${(pos['workerCount'] as int? ?? 1) > 1 ? 's' : ''}',
style: UiTypography.footnote2r.textSecondary,
),

View File

@@ -4,7 +4,7 @@ import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart';
import 'data/repositories/view_orders_repository_impl.dart';
import 'domain/repositories/i_view_orders_repository.dart';
import 'domain/repositories/view_orders_repository_interface.dart';
import 'domain/usecases/get_orders_use_case.dart';
import 'presentation/blocs/view_orders_cubit.dart';
import 'presentation/pages/view_orders_page.dart';
@@ -20,7 +20,7 @@ class ViewOrdersModule extends Module {
@override
void binds(Injector i) {
// Repositories
i.add<IViewOrdersRepository>(
i.add<ViewOrdersRepositoryInterface>(
() => ViewOrdersRepositoryImpl(
apiService: i.get<BaseApiService>(),
),