feat: Implement order details retrieval for pre-filling new order forms for reordering.

This commit is contained in:
Achintha Isuru
2026-02-22 02:18:33 -05:00
parent 3aab5bfc26
commit 214e0d1237
10 changed files with 594 additions and 197 deletions

View File

@@ -42,6 +42,7 @@ export 'src/entities/orders/permanent_order.dart';
export 'src/entities/orders/permanent_order_position.dart'; export 'src/entities/orders/permanent_order_position.dart';
export 'src/entities/orders/order_type.dart'; export 'src/entities/orders/order_type.dart';
export 'src/entities/orders/order_item.dart'; export 'src/entities/orders/order_item.dart';
export 'src/entities/orders/reorder_data.dart';
// Skills & Certs // Skills & Certs
export 'src/entities/skills/skill.dart'; export 'src/entities/skills/skill.dart';

View File

@@ -0,0 +1,76 @@
import 'package:equatable/equatable.dart';
import 'one_time_order.dart';
import 'order_type.dart';
/// Represents the full details of an order retrieved for reordering.
class ReorderData extends Equatable {
const ReorderData({
required this.orderId,
required this.orderType,
required this.eventName,
required this.vendorId,
required this.hub,
required this.positions,
this.date,
this.startDate,
this.endDate,
this.recurringDays = const <String>[],
this.permanentDays = const <String>[],
});
final String orderId;
final OrderType orderType;
final String eventName;
final String? vendorId;
final OneTimeOrderHubDetails hub;
final List<ReorderPosition> positions;
// One-time specific
final DateTime? date;
// Recurring/Permanent specific
final DateTime? startDate;
final DateTime? endDate;
final List<String> recurringDays;
final List<String> permanentDays;
@override
List<Object?> get props => <Object?>[
orderId,
orderType,
eventName,
vendorId,
hub,
positions,
date,
startDate,
endDate,
recurringDays,
permanentDays,
];
}
class ReorderPosition extends Equatable {
const ReorderPosition({
required this.roleId,
required this.count,
required this.startTime,
required this.endTime,
this.lunchBreak = 'NO_BREAK',
});
final String roleId;
final int count;
final String startTime;
final String endTime;
final String lunchBreak;
@override
List<Object?> get props => <Object?>[
roleId,
count,
startTime,
endTime,
lunchBreak,
];
}

View File

@@ -8,6 +8,7 @@ import 'domain/usecases/create_one_time_order_usecase.dart';
import 'domain/usecases/create_permanent_order_usecase.dart'; 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 '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';
@@ -27,13 +28,16 @@ class ClientCreateOrderModule extends Module {
@override @override
void binds(Injector i) { void binds(Injector i) {
// Repositories // Repositories
i.addLazySingleton<ClientCreateOrderRepositoryInterface>(ClientCreateOrderRepositoryImpl.new); i.addLazySingleton<ClientCreateOrderRepositoryInterface>(
ClientCreateOrderRepositoryImpl.new,
);
// UseCases // UseCases
i.addLazySingleton(CreateOneTimeOrderUseCase.new); i.addLazySingleton(CreateOneTimeOrderUseCase.new);
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(GetOrderDetailsForReorderUseCase.new);
// BLoCs // BLoCs
i.add<RapidOrderBloc>(RapidOrderBloc.new); i.add<RapidOrderBloc>(RapidOrderBloc.new);
@@ -49,19 +53,31 @@ class ClientCreateOrderModule extends Module {
child: (BuildContext context) => const ClientCreateOrderPage(), child: (BuildContext context) => const ClientCreateOrderPage(),
); );
r.child( r.child(
ClientPaths.childRoute(ClientPaths.createOrder, ClientPaths.createOrderRapid), ClientPaths.childRoute(
ClientPaths.createOrder,
ClientPaths.createOrderRapid,
),
child: (BuildContext context) => const RapidOrderPage(), child: (BuildContext context) => const RapidOrderPage(),
); );
r.child( r.child(
ClientPaths.childRoute(ClientPaths.createOrder, ClientPaths.createOrderOneTime), ClientPaths.childRoute(
ClientPaths.createOrder,
ClientPaths.createOrderOneTime,
),
child: (BuildContext context) => const OneTimeOrderPage(), child: (BuildContext context) => const OneTimeOrderPage(),
); );
r.child( r.child(
ClientPaths.childRoute(ClientPaths.createOrder, ClientPaths.createOrderRecurring), ClientPaths.childRoute(
ClientPaths.createOrder,
ClientPaths.createOrderRecurring,
),
child: (BuildContext context) => const RecurringOrderPage(), child: (BuildContext context) => const RecurringOrderPage(),
); );
r.child( r.child(
ClientPaths.childRoute(ClientPaths.createOrder, ClientPaths.createOrderPermanent), ClientPaths.childRoute(
ClientPaths.createOrder,
ClientPaths.createOrderPermanent,
),
child: (BuildContext context) => const PermanentOrderPage(), child: (BuildContext context) => const PermanentOrderPage(),
); );
} }

View File

@@ -1,4 +1,4 @@
import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc; import 'package:firebase_data_connect/firebase_data_connect.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:krow_data_connect/krow_data_connect.dart' as dc; import 'package:krow_data_connect/krow_data_connect.dart' as dc;
import 'package:krow_domain/krow_domain.dart' as domain; import 'package:krow_domain/krow_domain.dart' as domain;
@@ -11,10 +11,10 @@ import '../../domain/repositories/client_create_order_repository_interface.dart'
/// ///
/// It follows the KROW Clean Architecture by keeping the data layer focused /// It follows the KROW Clean Architecture by keeping the data layer focused
/// on delegation and data mapping, without business logic. /// on delegation and data mapping, without business logic.
class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInterface { class ClientCreateOrderRepositoryImpl
ClientCreateOrderRepositoryImpl({ implements ClientCreateOrderRepositoryInterface {
required dc.DataConnectService service, ClientCreateOrderRepositoryImpl({required dc.DataConnectService service})
}) : _service = service; : _service = service;
final dc.DataConnectService _service; final dc.DataConnectService _service;
@@ -36,19 +36,19 @@ class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInte
order.date.month, order.date.month,
order.date.day, order.date.day,
); );
final fdc.Timestamp orderTimestamp = _service.toTimestamp(orderDateOnly); final Timestamp orderTimestamp = _service.toTimestamp(orderDateOnly);
final fdc.OperationResult<dc.CreateOrderData, dc.CreateOrderVariables> orderResult = final OperationResult<dc.CreateOrderData, dc.CreateOrderVariables>
await _service.connector orderResult = await _service.connector
.createOrder( .createOrder(
businessId: businessId, businessId: businessId,
orderType: dc.OrderType.ONE_TIME, orderType: dc.OrderType.ONE_TIME,
teamHubId: hub.id, teamHubId: hub.id,
) )
.vendorId(vendorId) .vendorId(vendorId)
.eventName(order.eventName) .eventName(order.eventName)
.status(dc.OrderStatus.POSTED) .status(dc.OrderStatus.POSTED)
.date(orderTimestamp) .date(orderTimestamp)
.execute(); .execute();
final String orderId = orderResult.data.order_insert.id; final String orderId = orderResult.data.order_insert.id;
@@ -59,32 +59,34 @@ class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInte
final String shiftTitle = 'Shift 1 ${_formatDate(order.date)}'; final String shiftTitle = 'Shift 1 ${_formatDate(order.date)}';
final double shiftCost = _calculateShiftCost(order); final double shiftCost = _calculateShiftCost(order);
final fdc.OperationResult<dc.CreateShiftData, dc.CreateShiftVariables> shiftResult = final OperationResult<dc.CreateShiftData, dc.CreateShiftVariables>
await _service.connector shiftResult = await _service.connector
.createShift(title: shiftTitle, orderId: orderId) .createShift(title: shiftTitle, orderId: orderId)
.date(orderTimestamp) .date(orderTimestamp)
.location(hub.name) .location(hub.name)
.locationAddress(hub.address) .locationAddress(hub.address)
.latitude(hub.latitude) .latitude(hub.latitude)
.longitude(hub.longitude) .longitude(hub.longitude)
.placeId(hub.placeId) .placeId(hub.placeId)
.city(hub.city) .city(hub.city)
.state(hub.state) .state(hub.state)
.street(hub.street) .street(hub.street)
.country(hub.country) .country(hub.country)
.status(dc.ShiftStatus.OPEN) .status(dc.ShiftStatus.OPEN)
.workersNeeded(workersNeeded) .workersNeeded(workersNeeded)
.filled(0) .filled(0)
.durationDays(1) .durationDays(1)
.cost(shiftCost) .cost(shiftCost)
.execute(); .execute();
final String shiftId = shiftResult.data.shift_insert.id; final String shiftId = shiftResult.data.shift_insert.id;
for (final domain.OneTimeOrderPosition position in order.positions) { for (final domain.OneTimeOrderPosition position in order.positions) {
final DateTime start = _parseTime(order.date, position.startTime); final DateTime start = _parseTime(order.date, position.startTime);
final DateTime end = _parseTime(order.date, position.endTime); final DateTime end = _parseTime(order.date, position.endTime);
final DateTime normalizedEnd = end.isBefore(start) ? end.add(const Duration(days: 1)) : end; final DateTime normalizedEnd = end.isBefore(start)
? end.add(const Duration(days: 1))
: end;
final double hours = normalizedEnd.difference(start).inMinutes / 60.0; final double hours = normalizedEnd.difference(start).inMinutes / 60.0;
final double rate = order.roleRates[position.role] ?? 0; final double rate = order.roleRates[position.role] ?? 0;
final double totalValue = rate * hours * position.count; final double totalValue = rate * hours * position.count;
@@ -106,7 +108,7 @@ class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInte
await _service.connector await _service.connector
.updateOrder(id: orderId, teamHubId: hub.id) .updateOrder(id: orderId, teamHubId: hub.id)
.shifts(fdc.AnyValue(<String>[shiftId])) .shifts(AnyValue(<String>[shiftId]))
.execute(); .execute();
}); });
} }
@@ -129,74 +131,78 @@ class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInte
order.startDate.month, order.startDate.month,
order.startDate.day, order.startDate.day,
); );
final fdc.Timestamp orderTimestamp = _service.toTimestamp(orderDateOnly); final Timestamp orderTimestamp = _service.toTimestamp(orderDateOnly);
final fdc.Timestamp startTimestamp = orderTimestamp; final Timestamp startTimestamp = orderTimestamp;
final fdc.Timestamp endTimestamp = _service.toTimestamp(order.endDate); final Timestamp endTimestamp = _service.toTimestamp(order.endDate);
final fdc.OperationResult<dc.CreateOrderData, dc.CreateOrderVariables> orderResult = final OperationResult<dc.CreateOrderData, dc.CreateOrderVariables>
await _service.connector orderResult = await _service.connector
.createOrder( .createOrder(
businessId: businessId, businessId: businessId,
orderType: dc.OrderType.RECURRING, orderType: dc.OrderType.RECURRING,
teamHubId: hub.id, teamHubId: hub.id,
) )
.vendorId(vendorId) .vendorId(vendorId)
.eventName(order.eventName) .eventName(order.eventName)
.status(dc.OrderStatus.POSTED) .status(dc.OrderStatus.POSTED)
.date(orderTimestamp) .date(orderTimestamp)
.startDate(startTimestamp) .startDate(startTimestamp)
.endDate(endTimestamp) .endDate(endTimestamp)
.recurringDays(order.recurringDays) .recurringDays(order.recurringDays)
.execute(); .execute();
final String orderId = orderResult.data.order_insert.id; final String orderId = orderResult.data.order_insert.id;
// NOTE: Recurring orders are limited to 30 days of generated shifts. // NOTE: Recurring orders are limited to 30 days of generated shifts.
// Future shifts beyond 30 days should be created by a scheduled job. // Future shifts beyond 30 days should be created by a scheduled job.
final DateTime maxEndDate = orderDateOnly.add(const Duration(days: 29)); final DateTime maxEndDate = orderDateOnly.add(const Duration(days: 29));
final DateTime effectiveEndDate = final DateTime effectiveEndDate = order.endDate.isAfter(maxEndDate)
order.endDate.isAfter(maxEndDate) ? maxEndDate : order.endDate; ? maxEndDate
: order.endDate;
final Set<String> selectedDays = Set<String>.from(order.recurringDays); final Set<String> selectedDays = Set<String>.from(order.recurringDays);
final int workersNeeded = order.positions.fold<int>( final int workersNeeded = order.positions.fold<int>(
0, 0,
(int sum, domain.RecurringOrderPosition position) => sum + position.count, (int sum, domain.RecurringOrderPosition position) =>
sum + position.count,
); );
final double shiftCost = _calculateRecurringShiftCost(order); final double shiftCost = _calculateRecurringShiftCost(order);
final List<String> shiftIds = <String>[]; final List<String> shiftIds = <String>[];
for (DateTime day = orderDateOnly; for (
!day.isAfter(effectiveEndDate); DateTime day = orderDateOnly;
day = day.add(const Duration(days: 1))) { !day.isAfter(effectiveEndDate);
day = day.add(const Duration(days: 1))
) {
final String dayLabel = _weekdayLabel(day); final String dayLabel = _weekdayLabel(day);
if (!selectedDays.contains(dayLabel)) { if (!selectedDays.contains(dayLabel)) {
continue; continue;
} }
final String shiftTitle = 'Shift ${_formatDate(day)}'; final String shiftTitle = 'Shift ${_formatDate(day)}';
final fdc.Timestamp dayTimestamp = _service.toTimestamp( final Timestamp dayTimestamp = _service.toTimestamp(
DateTime(day.year, day.month, day.day), DateTime(day.year, day.month, day.day),
); );
final fdc.OperationResult<dc.CreateShiftData, dc.CreateShiftVariables> shiftResult = final OperationResult<dc.CreateShiftData, dc.CreateShiftVariables>
await _service.connector shiftResult = await _service.connector
.createShift(title: shiftTitle, orderId: orderId) .createShift(title: shiftTitle, orderId: orderId)
.date(dayTimestamp) .date(dayTimestamp)
.location(hub.name) .location(hub.name)
.locationAddress(hub.address) .locationAddress(hub.address)
.latitude(hub.latitude) .latitude(hub.latitude)
.longitude(hub.longitude) .longitude(hub.longitude)
.placeId(hub.placeId) .placeId(hub.placeId)
.city(hub.city) .city(hub.city)
.state(hub.state) .state(hub.state)
.street(hub.street) .street(hub.street)
.country(hub.country) .country(hub.country)
.status(dc.ShiftStatus.OPEN) .status(dc.ShiftStatus.OPEN)
.workersNeeded(workersNeeded) .workersNeeded(workersNeeded)
.filled(0) .filled(0)
.durationDays(1) .durationDays(1)
.cost(shiftCost) .cost(shiftCost)
.execute(); .execute();
final String shiftId = shiftResult.data.shift_insert.id; final String shiftId = shiftResult.data.shift_insert.id;
shiftIds.add(shiftId); shiftIds.add(shiftId);
@@ -204,8 +210,9 @@ class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInte
for (final domain.RecurringOrderPosition position in order.positions) { for (final domain.RecurringOrderPosition position in order.positions) {
final DateTime start = _parseTime(day, position.startTime); final DateTime start = _parseTime(day, position.startTime);
final DateTime end = _parseTime(day, position.endTime); final DateTime end = _parseTime(day, position.endTime);
final DateTime normalizedEnd = final DateTime normalizedEnd = end.isBefore(start)
end.isBefore(start) ? end.add(const Duration(days: 1)) : end; ? end.add(const Duration(days: 1))
: end;
final double hours = normalizedEnd.difference(start).inMinutes / 60.0; final double hours = normalizedEnd.difference(start).inMinutes / 60.0;
final double rate = order.roleRates[position.role] ?? 0; final double rate = order.roleRates[position.role] ?? 0;
final double totalValue = rate * hours * position.count; final double totalValue = rate * hours * position.count;
@@ -228,7 +235,7 @@ class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInte
await _service.connector await _service.connector
.updateOrder(id: orderId, teamHubId: hub.id) .updateOrder(id: orderId, teamHubId: hub.id)
.shifts(fdc.AnyValue(shiftIds)) .shifts(AnyValue(shiftIds))
.execute(); .execute();
}); });
} }
@@ -251,23 +258,23 @@ class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInte
order.startDate.month, order.startDate.month,
order.startDate.day, order.startDate.day,
); );
final fdc.Timestamp orderTimestamp = _service.toTimestamp(orderDateOnly); final Timestamp orderTimestamp = _service.toTimestamp(orderDateOnly);
final fdc.Timestamp startTimestamp = orderTimestamp; final Timestamp startTimestamp = orderTimestamp;
final fdc.OperationResult<dc.CreateOrderData, dc.CreateOrderVariables> orderResult = final OperationResult<dc.CreateOrderData, dc.CreateOrderVariables>
await _service.connector orderResult = await _service.connector
.createOrder( .createOrder(
businessId: businessId, businessId: businessId,
orderType: dc.OrderType.PERMANENT, orderType: dc.OrderType.PERMANENT,
teamHubId: hub.id, teamHubId: hub.id,
) )
.vendorId(vendorId) .vendorId(vendorId)
.eventName(order.eventName) .eventName(order.eventName)
.status(dc.OrderStatus.POSTED) .status(dc.OrderStatus.POSTED)
.date(orderTimestamp) .date(orderTimestamp)
.startDate(startTimestamp) .startDate(startTimestamp)
.permanentDays(order.permanentDays) .permanentDays(order.permanentDays)
.execute(); .execute();
final String orderId = orderResult.data.order_insert.id; final String orderId = orderResult.data.order_insert.id;
@@ -283,38 +290,40 @@ class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInte
final double shiftCost = _calculatePermanentShiftCost(order); final double shiftCost = _calculatePermanentShiftCost(order);
final List<String> shiftIds = <String>[]; final List<String> shiftIds = <String>[];
for (DateTime day = orderDateOnly; for (
!day.isAfter(maxEndDate); DateTime day = orderDateOnly;
day = day.add(const Duration(days: 1))) { !day.isAfter(maxEndDate);
day = day.add(const Duration(days: 1))
) {
final String dayLabel = _weekdayLabel(day); final String dayLabel = _weekdayLabel(day);
if (!selectedDays.contains(dayLabel)) { if (!selectedDays.contains(dayLabel)) {
continue; continue;
} }
final String shiftTitle = 'Shift ${_formatDate(day)}'; final String shiftTitle = 'Shift ${_formatDate(day)}';
final fdc.Timestamp dayTimestamp = _service.toTimestamp( final Timestamp dayTimestamp = _service.toTimestamp(
DateTime(day.year, day.month, day.day), DateTime(day.year, day.month, day.day),
); );
final fdc.OperationResult<dc.CreateShiftData, dc.CreateShiftVariables> shiftResult = final OperationResult<dc.CreateShiftData, dc.CreateShiftVariables>
await _service.connector shiftResult = await _service.connector
.createShift(title: shiftTitle, orderId: orderId) .createShift(title: shiftTitle, orderId: orderId)
.date(dayTimestamp) .date(dayTimestamp)
.location(hub.name) .location(hub.name)
.locationAddress(hub.address) .locationAddress(hub.address)
.latitude(hub.latitude) .latitude(hub.latitude)
.longitude(hub.longitude) .longitude(hub.longitude)
.placeId(hub.placeId) .placeId(hub.placeId)
.city(hub.city) .city(hub.city)
.state(hub.state) .state(hub.state)
.street(hub.street) .street(hub.street)
.country(hub.country) .country(hub.country)
.status(dc.ShiftStatus.OPEN) .status(dc.ShiftStatus.OPEN)
.workersNeeded(workersNeeded) .workersNeeded(workersNeeded)
.filled(0) .filled(0)
.durationDays(1) .durationDays(1)
.cost(shiftCost) .cost(shiftCost)
.execute(); .execute();
final String shiftId = shiftResult.data.shift_insert.id; final String shiftId = shiftResult.data.shift_insert.id;
shiftIds.add(shiftId); shiftIds.add(shiftId);
@@ -322,8 +331,9 @@ class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInte
for (final domain.OneTimeOrderPosition position in order.positions) { for (final domain.OneTimeOrderPosition position in order.positions) {
final DateTime start = _parseTime(day, position.startTime); final DateTime start = _parseTime(day, position.startTime);
final DateTime end = _parseTime(day, position.endTime); final DateTime end = _parseTime(day, position.endTime);
final DateTime normalizedEnd = final DateTime normalizedEnd = end.isBefore(start)
end.isBefore(start) ? end.add(const Duration(days: 1)) : end; ? end.add(const Duration(days: 1))
: end;
final double hours = normalizedEnd.difference(start).inMinutes / 60.0; final double hours = normalizedEnd.difference(start).inMinutes / 60.0;
final double rate = order.roleRates[position.role] ?? 0; final double rate = order.roleRates[position.role] ?? 0;
final double totalValue = rate * hours * position.count; final double totalValue = rate * hours * position.count;
@@ -346,7 +356,7 @@ class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInte
await _service.connector await _service.connector
.updateOrder(id: orderId, teamHubId: hub.id) .updateOrder(id: orderId, teamHubId: hub.id)
.shifts(fdc.AnyValue(shiftIds)) .shifts(AnyValue(shiftIds))
.execute(); .execute();
}); });
} }
@@ -363,13 +373,76 @@ class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInte
throw UnimplementedError('Reorder functionality is not yet implemented.'); throw UnimplementedError('Reorder functionality is not yet implemented.');
} }
@override
Future<domain.ReorderData> getOrderDetailsForReorder(String orderId) async {
return _service.run(() async {
final String businessId = await _service.getBusinessId();
final QueryResult<
dc.ListShiftRolesByBusinessAndOrderData,
dc.ListShiftRolesByBusinessAndOrderVariables
>
result = await _service.connector
.listShiftRolesByBusinessAndOrder(
businessId: businessId,
orderId: orderId,
)
.execute();
final List<dc.ListShiftRolesByBusinessAndOrderShiftRoles> shiftRoles =
result.data.shiftRoles;
if (shiftRoles.isEmpty) {
throw Exception('Order not found or has no roles.');
}
final dc.ListShiftRolesByBusinessAndOrderShiftRolesShiftOrder order =
shiftRoles.first.shift.order;
final domain.OrderType orderType = _mapOrderType(order.orderType);
final dc.ListShiftRolesByBusinessAndOrderShiftRolesShiftOrderTeamHub
teamHub = order.teamHub;
return domain.ReorderData(
orderId: orderId,
eventName: order.eventName ?? '',
vendorId: order.vendorId ?? '',
orderType: orderType,
hub: domain.OneTimeOrderHubDetails(
id: teamHub.id,
name: teamHub.hubName,
address: teamHub.address,
placeId: teamHub.placeId,
latitude: 0, // Not available in this query
longitude: 0,
),
positions: shiftRoles.map((
dc.ListShiftRolesByBusinessAndOrderShiftRoles role,
) {
return domain.ReorderPosition(
roleId: role.roleId,
count: role.count,
startTime: _formatTimestamp(role.startTime),
endTime: _formatTimestamp(role.endTime),
lunchBreak: _formatBreakDuration(role.breakType),
);
}).toList(),
startDate: order.startDate?.toDateTime(),
endDate: order.endDate?.toDateTime(),
recurringDays: order.recurringDays ?? const <String>[],
permanentDays: order.permanentDays ?? const <String>[],
);
});
}
double _calculateShiftCost(domain.OneTimeOrder order) { double _calculateShiftCost(domain.OneTimeOrder order) {
double total = 0; double total = 0;
for (final domain.OneTimeOrderPosition position in order.positions) { for (final domain.OneTimeOrderPosition position in order.positions) {
final DateTime start = _parseTime(order.date, position.startTime); final DateTime start = _parseTime(order.date, position.startTime);
final DateTime end = _parseTime(order.date, position.endTime); final DateTime end = _parseTime(order.date, position.endTime);
final DateTime normalizedEnd = final DateTime normalizedEnd = end.isBefore(start)
end.isBefore(start) ? end.add(const Duration(days: 1)) : end; ? end.add(const Duration(days: 1))
: end;
final double hours = normalizedEnd.difference(start).inMinutes / 60.0; final double hours = normalizedEnd.difference(start).inMinutes / 60.0;
final double rate = order.roleRates[position.role] ?? 0; final double rate = order.roleRates[position.role] ?? 0;
total += rate * hours * position.count; total += rate * hours * position.count;
@@ -382,8 +455,9 @@ class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInte
for (final domain.RecurringOrderPosition position in order.positions) { for (final domain.RecurringOrderPosition position in order.positions) {
final DateTime start = _parseTime(order.startDate, position.startTime); final DateTime start = _parseTime(order.startDate, position.startTime);
final DateTime end = _parseTime(order.startDate, position.endTime); final DateTime end = _parseTime(order.startDate, position.endTime);
final DateTime normalizedEnd = final DateTime normalizedEnd = end.isBefore(start)
end.isBefore(start) ? end.add(const Duration(days: 1)) : end; ? end.add(const Duration(days: 1))
: end;
final double hours = normalizedEnd.difference(start).inMinutes / 60.0; final double hours = normalizedEnd.difference(start).inMinutes / 60.0;
final double rate = order.roleRates[position.role] ?? 0; final double rate = order.roleRates[position.role] ?? 0;
total += rate * hours * position.count; total += rate * hours * position.count;
@@ -396,8 +470,9 @@ class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInte
for (final domain.OneTimeOrderPosition position in order.positions) { for (final domain.OneTimeOrderPosition position in order.positions) {
final DateTime start = _parseTime(order.startDate, position.startTime); final DateTime start = _parseTime(order.startDate, position.startTime);
final DateTime end = _parseTime(order.startDate, position.endTime); final DateTime end = _parseTime(order.startDate, position.endTime);
final DateTime normalizedEnd = final DateTime normalizedEnd = end.isBefore(start)
end.isBefore(start) ? end.add(const Duration(days: 1)) : end; ? end.add(const Duration(days: 1))
: end;
final double hours = normalizedEnd.difference(start).inMinutes / 60.0; final double hours = normalizedEnd.difference(start).inMinutes / 60.0;
final double rate = order.roleRates[position.role] ?? 0; final double rate = order.roleRates[position.role] ?? 0;
total += rate * hours * position.count; total += rate * hours * position.count;
@@ -473,4 +548,49 @@ class ClientCreateOrderRepositoryImpl implements ClientCreateOrderRepositoryInte
final String day = dateTime.day.toString().padLeft(2, '0'); final String day = dateTime.day.toString().padLeft(2, '0');
return '$year-$month-$day'; return '$year-$month-$day';
} }
String _formatTimestamp(Timestamp? value) {
if (value == null) return '';
try {
return DateFormat('HH:mm').format(value.toDateTime());
} catch (_) {
return '';
}
}
String _formatBreakDuration(dc.EnumValue<dc.BreakDuration>? breakType) {
if (breakType is dc.Known<dc.BreakDuration>) {
switch (breakType.value) {
case dc.BreakDuration.MIN_10:
return 'MIN_10';
case dc.BreakDuration.MIN_15:
return 'MIN_15';
case dc.BreakDuration.MIN_30:
return 'MIN_30';
case dc.BreakDuration.MIN_45:
return 'MIN_45';
case dc.BreakDuration.MIN_60:
return 'MIN_60';
case dc.BreakDuration.NO_BREAK:
return 'NO_BREAK';
}
}
return 'NO_BREAK';
}
domain.OrderType _mapOrderType(dc.EnumValue<dc.OrderType>? orderType) {
if (orderType is dc.Known<dc.OrderType>) {
switch (orderType.value) {
case dc.OrderType.ONE_TIME:
return domain.OrderType.oneTime;
case dc.OrderType.RECURRING:
return domain.OrderType.recurring;
case dc.OrderType.PERMANENT:
return domain.OrderType.permanent;
case dc.OrderType.RAPID:
return domain.OrderType.oneTime;
}
}
return domain.OrderType.oneTime;
}
} }

View File

@@ -29,4 +29,9 @@ abstract interface class ClientCreateOrderRepositoryInterface {
/// [previousOrderId] is the ID of the order to reorder. /// [previousOrderId] is the ID of the order to reorder.
/// [newDate] is the new date for the order. /// [newDate] is the new date for the order.
Future<void> reorder(String previousOrderId, DateTime newDate); Future<void> reorder(String previousOrderId, DateTime newDate);
/// Fetches the details of an existing order to be used as a template for a new order.
///
/// returns [ReorderData] containing the order details and positions.
Future<ReorderData> getOrderDetailsForReorder(String orderId);
} }

View File

@@ -0,0 +1,14 @@
import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart';
import '../repositories/client_create_order_repository_interface.dart';
/// Use case for fetching order details for reordering.
class GetOrderDetailsForReorderUseCase implements UseCase<String, ReorderData> {
const GetOrderDetailsForReorderUseCase(this._repository);
final ClientCreateOrderRepositoryInterface _repository;
@override
Future<ReorderData> call(String orderId) {
return _repository.getOrderDetailsForReorder(orderId);
}
}

View File

@@ -1,6 +1,7 @@
import 'package:client_create_order/src/domain/arguments/one_time_order_arguments.dart'; import 'package:client_create_order/src/domain/arguments/one_time_order_arguments.dart';
import 'package:client_create_order/src/domain/usecases/create_one_time_order_usecase.dart'; import 'package:client_create_order/src/domain/usecases/create_one_time_order_usecase.dart';
import 'package:firebase_data_connect/firebase_data_connect.dart'; import 'package:client_create_order/src/domain/usecases/get_order_details_for_reorder_usecase.dart';
import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc;
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_data_connect/krow_data_connect.dart' as dc; import 'package:krow_data_connect/krow_data_connect.dart' as dc;
@@ -14,8 +15,11 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
with with
BlocErrorHandler<OneTimeOrderState>, BlocErrorHandler<OneTimeOrderState>,
SafeBloc<OneTimeOrderEvent, OneTimeOrderState> { SafeBloc<OneTimeOrderEvent, OneTimeOrderState> {
OneTimeOrderBloc(this._createOneTimeOrderUseCase, this._service) OneTimeOrderBloc(
: super(OneTimeOrderState.initial()) { this._createOneTimeOrderUseCase,
this._getOrderDetailsForReorderUseCase,
this._service,
) : super(OneTimeOrderState.initial()) {
on<OneTimeOrderVendorsLoaded>(_onVendorsLoaded); on<OneTimeOrderVendorsLoaded>(_onVendorsLoaded);
on<OneTimeOrderVendorChanged>(_onVendorChanged); on<OneTimeOrderVendorChanged>(_onVendorChanged);
on<OneTimeOrderHubsLoaded>(_onHubsLoaded); on<OneTimeOrderHubsLoaded>(_onHubsLoaded);
@@ -32,12 +36,13 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
_loadHubs(); _loadHubs();
} }
final CreateOneTimeOrderUseCase _createOneTimeOrderUseCase; final CreateOneTimeOrderUseCase _createOneTimeOrderUseCase;
final GetOrderDetailsForReorderUseCase _getOrderDetailsForReorderUseCase;
final dc.DataConnectService _service; final dc.DataConnectService _service;
Future<void> _loadVendors() async { Future<void> _loadVendors() async {
final List<Vendor>? vendors = await handleErrorWithResult( final List<Vendor>? vendors = await handleErrorWithResult(
action: () async { action: () async {
final QueryResult<dc.ListVendorsData, void> result = await _service final fdc.QueryResult<dc.ListVendorsData, void> result = await _service
.connector .connector
.listVendors() .listVendors()
.execute(); .execute();
@@ -65,7 +70,7 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
) async { ) async {
final List<OneTimeOrderRoleOption>? roles = await handleErrorWithResult( final List<OneTimeOrderRoleOption>? roles = await handleErrorWithResult(
action: () async { action: () async {
final QueryResult< final fdc.QueryResult<
dc.ListRolesByVendorIdData, dc.ListRolesByVendorIdData,
dc.ListRolesByVendorIdVariables dc.ListRolesByVendorIdVariables
> >
@@ -95,7 +100,7 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
final List<OneTimeOrderHubOption>? hubs = await handleErrorWithResult( final List<OneTimeOrderHubOption>? hubs = await handleErrorWithResult(
action: () async { action: () async {
final String businessId = await _service.getBusinessId(); final String businessId = await _service.getBusinessId();
final QueryResult< final fdc.QueryResult<
dc.ListTeamHubsByOwnerIdData, dc.ListTeamHubsByOwnerIdData,
dc.ListTeamHubsByOwnerIdVariables dc.ListTeamHubsByOwnerIdVariables
> >
@@ -274,27 +279,72 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
); );
} }
void _onInitialized( Future<void> _onInitialized(
OneTimeOrderInitialized event, OneTimeOrderInitialized event,
Emitter<OneTimeOrderState> emit, Emitter<OneTimeOrderState> emit,
) { ) async {
final Map<String, dynamic> data = event.data; final Map<String, dynamic> data = event.data;
final String title = data['title']?.toString() ?? ''; final String title = data['title']?.toString() ?? '';
final int workers = (data['workers'] as int?) ?? 1;
final DateTime? startDate = data['startDate'] as DateTime?; final DateTime? startDate = data['startDate'] as DateTime?;
final String? orderId = data['orderId']?.toString();
emit( emit(state.copyWith(eventName: title, date: startDate ?? DateTime.now()));
state.copyWith(
eventName: title, if (orderId == null || orderId.isEmpty) return;
date: startDate ?? DateTime.now(),
positions: <OneTimeOrderPosition>[ emit(state.copyWith(status: OneTimeOrderStatus.loading));
OneTimeOrderPosition(
role: data['roleName']?.toString() ?? '', await handleError(
count: workers, emit: emit.call,
startTime: data['startTime']?.toString() ?? '09:00', action: () async {
endTime: data['endTime']?.toString() ?? '17:00', final ReorderData orderDetails =
await _getOrderDetailsForReorderUseCase(orderId);
// Map positions
final List<OneTimeOrderPosition> positions = orderDetails.positions.map(
(ReorderPosition role) {
return OneTimeOrderPosition(
role: role.roleId,
count: role.count,
startTime: role.startTime,
endTime: role.endTime,
lunchBreak: role.lunchBreak,
);
},
).toList();
// Update state with order details
final Vendor? selectedVendor = state.vendors
.where((Vendor v) => v.id == orderDetails.vendorId)
.firstOrNull;
final OneTimeOrderHubOption? selectedHub = state.hubs
.where(
(OneTimeOrderHubOption h) =>
h.placeId == orderDetails.hub.placeId,
)
.firstOrNull;
emit(
state.copyWith(
eventName: orderDetails.eventName.isNotEmpty
? orderDetails.eventName
: title,
positions: positions,
selectedVendor: selectedVendor,
selectedHub: selectedHub,
location: selectedHub?.name ?? '',
status: OneTimeOrderStatus.initial,
), ),
], );
if (selectedVendor != null) {
await _loadRolesForVendor(selectedVendor.id, emit);
}
},
onError: (String errorKey) => state.copyWith(
status: OneTimeOrderStatus.failure,
errorMessage: errorKey,
), ),
); );
} }

View File

@@ -1,5 +1,6 @@
import 'package:client_create_order/src/domain/usecases/create_permanent_order_usecase.dart'; import 'package:client_create_order/src/domain/usecases/create_permanent_order_usecase.dart';
import 'package:firebase_data_connect/firebase_data_connect.dart'; import 'package:client_create_order/src/domain/usecases/get_order_details_for_reorder_usecase.dart';
import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc;
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_data_connect/krow_data_connect.dart' as dc; import 'package:krow_data_connect/krow_data_connect.dart' as dc;
@@ -13,8 +14,11 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
with with
BlocErrorHandler<PermanentOrderState>, BlocErrorHandler<PermanentOrderState>,
SafeBloc<PermanentOrderEvent, PermanentOrderState> { SafeBloc<PermanentOrderEvent, PermanentOrderState> {
PermanentOrderBloc(this._createPermanentOrderUseCase, this._service) PermanentOrderBloc(
: super(PermanentOrderState.initial()) { this._createPermanentOrderUseCase,
this._getOrderDetailsForReorderUseCase,
this._service,
) : super(PermanentOrderState.initial()) {
on<PermanentOrderVendorsLoaded>(_onVendorsLoaded); on<PermanentOrderVendorsLoaded>(_onVendorsLoaded);
on<PermanentOrderVendorChanged>(_onVendorChanged); on<PermanentOrderVendorChanged>(_onVendorChanged);
on<PermanentOrderHubsLoaded>(_onHubsLoaded); on<PermanentOrderHubsLoaded>(_onHubsLoaded);
@@ -33,6 +37,7 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
} }
final CreatePermanentOrderUseCase _createPermanentOrderUseCase; final CreatePermanentOrderUseCase _createPermanentOrderUseCase;
final GetOrderDetailsForReorderUseCase _getOrderDetailsForReorderUseCase;
final dc.DataConnectService _service; final dc.DataConnectService _service;
static const List<String> _dayLabels = <String>[ static const List<String> _dayLabels = <String>[
@@ -48,7 +53,7 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
Future<void> _loadVendors() async { Future<void> _loadVendors() async {
final List<domain.Vendor>? vendors = await handleErrorWithResult( final List<domain.Vendor>? vendors = await handleErrorWithResult(
action: () async { action: () async {
final QueryResult<dc.ListVendorsData, void> result = await _service final fdc.QueryResult<dc.ListVendorsData, void> result = await _service
.connector .connector
.listVendors() .listVendors()
.execute(); .execute();
@@ -76,7 +81,7 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
) async { ) async {
final List<PermanentOrderRoleOption>? roles = await handleErrorWithResult( final List<PermanentOrderRoleOption>? roles = await handleErrorWithResult(
action: () async { action: () async {
final QueryResult< final fdc.QueryResult<
dc.ListRolesByVendorIdData, dc.ListRolesByVendorIdData,
dc.ListRolesByVendorIdVariables dc.ListRolesByVendorIdVariables
> >
@@ -106,7 +111,7 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
final List<PermanentOrderHubOption>? hubs = await handleErrorWithResult( final List<PermanentOrderHubOption>? hubs = await handleErrorWithResult(
action: () async { action: () async {
final String businessId = await _service.getBusinessId(); final String businessId = await _service.getBusinessId();
final QueryResult< final fdc.QueryResult<
dc.ListTeamHubsByOwnerIdData, dc.ListTeamHubsByOwnerIdData,
dc.ListTeamHubsByOwnerIdVariables dc.ListTeamHubsByOwnerIdVariables
> >
@@ -337,27 +342,76 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
); );
} }
void _onInitialized( Future<void> _onInitialized(
PermanentOrderInitialized event, PermanentOrderInitialized event,
Emitter<PermanentOrderState> emit, Emitter<PermanentOrderState> emit,
) { ) async {
final Map<String, dynamic> data = event.data; final Map<String, dynamic> data = event.data;
final String title = data['title']?.toString() ?? ''; final String title = data['title']?.toString() ?? '';
final int workers = (data['workers'] as int?) ?? 1;
final DateTime? startDate = data['startDate'] as DateTime?; final DateTime? startDate = data['startDate'] as DateTime?;
final String? orderId = data['orderId']?.toString();
emit( emit(
state.copyWith( state.copyWith(eventName: title, startDate: startDate ?? DateTime.now()),
eventName: title, );
startDate: startDate ?? DateTime.now(),
positions: <PermanentOrderPosition>[ if (orderId == null || orderId.isEmpty) return;
PermanentOrderPosition(
role: data['roleName']?.toString() ?? '', emit(state.copyWith(status: PermanentOrderStatus.loading));
count: workers,
startTime: data['startTime']?.toString() ?? '09:00', await handleError(
endTime: data['endTime']?.toString() ?? '17:00', emit: emit.call,
action: () async {
final domain.ReorderData orderDetails =
await _getOrderDetailsForReorderUseCase(orderId);
// Map positions
final List<PermanentOrderPosition> positions = orderDetails.positions
.map((domain.ReorderPosition role) {
return PermanentOrderPosition(
role: role.roleId,
count: role.count,
startTime: role.startTime,
endTime: role.endTime,
lunchBreak: role.lunchBreak,
);
})
.toList();
// Update state with order details
final domain.Vendor? selectedVendor = state.vendors
.where((domain.Vendor v) => v.id == orderDetails.vendorId)
.firstOrNull;
final PermanentOrderHubOption? selectedHub = state.hubs
.where(
(PermanentOrderHubOption h) =>
h.placeId == orderDetails.hub.placeId,
)
.firstOrNull;
emit(
state.copyWith(
eventName: orderDetails.eventName.isNotEmpty
? orderDetails.eventName
: title,
positions: positions,
selectedVendor: selectedVendor,
selectedHub: selectedHub,
location: selectedHub?.name ?? '',
status: PermanentOrderStatus.initial,
startDate: startDate ?? orderDetails.startDate ?? DateTime.now(),
permanentDays: orderDetails.permanentDays,
), ),
], );
if (selectedVendor != null) {
await _loadRolesForVendor(selectedVendor.id, emit);
}
},
onError: (String errorKey) => state.copyWith(
status: PermanentOrderStatus.failure,
errorMessage: errorKey,
), ),
); );
} }

View File

@@ -1,5 +1,6 @@
import 'package:client_create_order/src/domain/usecases/create_recurring_order_usecase.dart'; import 'package:client_create_order/src/domain/usecases/create_recurring_order_usecase.dart';
import 'package:firebase_data_connect/firebase_data_connect.dart'; import 'package:client_create_order/src/domain/usecases/get_order_details_for_reorder_usecase.dart';
import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc;
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_data_connect/krow_data_connect.dart' as dc; import 'package:krow_data_connect/krow_data_connect.dart' as dc;
@@ -13,8 +14,11 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
with with
BlocErrorHandler<RecurringOrderState>, BlocErrorHandler<RecurringOrderState>,
SafeBloc<RecurringOrderEvent, RecurringOrderState> { SafeBloc<RecurringOrderEvent, RecurringOrderState> {
RecurringOrderBloc(this._createRecurringOrderUseCase, this._service) RecurringOrderBloc(
: super(RecurringOrderState.initial()) { this._createRecurringOrderUseCase,
this._getOrderDetailsForReorderUseCase,
this._service,
) : super(RecurringOrderState.initial()) {
on<RecurringOrderVendorsLoaded>(_onVendorsLoaded); on<RecurringOrderVendorsLoaded>(_onVendorsLoaded);
on<RecurringOrderVendorChanged>(_onVendorChanged); on<RecurringOrderVendorChanged>(_onVendorChanged);
on<RecurringOrderHubsLoaded>(_onHubsLoaded); on<RecurringOrderHubsLoaded>(_onHubsLoaded);
@@ -34,6 +38,7 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
} }
final CreateRecurringOrderUseCase _createRecurringOrderUseCase; final CreateRecurringOrderUseCase _createRecurringOrderUseCase;
final GetOrderDetailsForReorderUseCase _getOrderDetailsForReorderUseCase;
final dc.DataConnectService _service; final dc.DataConnectService _service;
static const List<String> _dayLabels = <String>[ static const List<String> _dayLabels = <String>[
@@ -49,7 +54,7 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
Future<void> _loadVendors() async { Future<void> _loadVendors() async {
final List<domain.Vendor>? vendors = await handleErrorWithResult( final List<domain.Vendor>? vendors = await handleErrorWithResult(
action: () async { action: () async {
final QueryResult<dc.ListVendorsData, void> result = await _service final fdc.QueryResult<dc.ListVendorsData, void> result = await _service
.connector .connector
.listVendors() .listVendors()
.execute(); .execute();
@@ -77,7 +82,7 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
) async { ) async {
final List<RecurringOrderRoleOption>? roles = await handleErrorWithResult( final List<RecurringOrderRoleOption>? roles = await handleErrorWithResult(
action: () async { action: () async {
final QueryResult< final fdc.QueryResult<
dc.ListRolesByVendorIdData, dc.ListRolesByVendorIdData,
dc.ListRolesByVendorIdVariables dc.ListRolesByVendorIdVariables
> >
@@ -107,7 +112,7 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
final List<RecurringOrderHubOption>? hubs = await handleErrorWithResult( final List<RecurringOrderHubOption>? hubs = await handleErrorWithResult(
action: () async { action: () async {
final String businessId = await _service.getBusinessId(); final String businessId = await _service.getBusinessId();
final QueryResult< final fdc.QueryResult<
dc.ListTeamHubsByOwnerIdData, dc.ListTeamHubsByOwnerIdData,
dc.ListTeamHubsByOwnerIdVariables dc.ListTeamHubsByOwnerIdVariables
> >
@@ -356,27 +361,77 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
); );
} }
void _onInitialized( Future<void> _onInitialized(
RecurringOrderInitialized event, RecurringOrderInitialized event,
Emitter<RecurringOrderState> emit, Emitter<RecurringOrderState> emit,
) { ) async {
final Map<String, dynamic> data = event.data; final Map<String, dynamic> data = event.data;
final String title = data['title']?.toString() ?? ''; final String title = data['title']?.toString() ?? '';
final int workers = (data['workers'] as int?) ?? 1;
final DateTime? startDate = data['startDate'] as DateTime?; final DateTime? startDate = data['startDate'] as DateTime?;
final String? orderId = data['orderId']?.toString();
emit( emit(
state.copyWith( state.copyWith(eventName: title, startDate: startDate ?? DateTime.now()),
eventName: title, );
startDate: startDate ?? DateTime.now(),
positions: <RecurringOrderPosition>[ if (orderId == null || orderId.isEmpty) return;
RecurringOrderPosition(
role: data['roleName']?.toString() ?? '', emit(state.copyWith(status: RecurringOrderStatus.loading));
count: workers,
startTime: data['startTime']?.toString() ?? '09:00', await handleError(
endTime: data['endTime']?.toString() ?? '17:00', emit: emit.call,
action: () async {
final domain.ReorderData orderDetails =
await _getOrderDetailsForReorderUseCase(orderId);
// Map positions
final List<RecurringOrderPosition> positions = orderDetails.positions
.map((domain.ReorderPosition role) {
return RecurringOrderPosition(
role: role.roleId,
count: role.count,
startTime: role.startTime,
endTime: role.endTime,
lunchBreak: role.lunchBreak,
);
})
.toList();
// Update state with order details
final domain.Vendor? selectedVendor = state.vendors
.where((domain.Vendor v) => v.id == orderDetails.vendorId)
.firstOrNull;
final RecurringOrderHubOption? selectedHub = state.hubs
.where(
(RecurringOrderHubOption h) =>
h.placeId == orderDetails.hub.placeId,
)
.firstOrNull;
emit(
state.copyWith(
eventName: orderDetails.eventName.isNotEmpty
? orderDetails.eventName
: title,
positions: positions,
selectedVendor: selectedVendor,
selectedHub: selectedHub,
location: selectedHub?.name ?? '',
status: RecurringOrderStatus.initial,
startDate: startDate ?? orderDetails.startDate ?? DateTime.now(),
endDate: orderDetails.endDate ?? DateTime.now(),
recurringDays: orderDetails.recurringDays,
), ),
], );
if (selectedVendor != null) {
await _loadRolesForVendor(selectedVendor.id, emit);
}
},
onError: (String errorKey) => state.copyWith(
status: RecurringOrderStatus.failure,
errorMessage: errorKey,
), ),
); );
} }

View File

@@ -404,9 +404,15 @@ query listShiftRolesByBusinessAndOrder(
vendorId vendorId
eventName eventName
date date
startDate
endDate
recurringDays
permanentDays
orderType
#location #location
teamHub { teamHub {
id
address address
placeId placeId
hubName hubName