feat: Implement UTC conversion for order date and time serialization in order use cases

This commit is contained in:
Achintha Isuru
2026-03-19 23:34:29 -04:00
parent 207831eb3e
commit 4cd83a9281
9 changed files with 186 additions and 135 deletions

View File

@@ -63,6 +63,39 @@ class OneTimeOrderArguments extends UseCaseArgument {
/// The selected vendor ID, if applicable.
final String? vendorId;
/// Serialises these arguments into the V2 API payload shape.
///
/// Times and dates are converted to UTC so the backend's
/// `combineDateAndTime` helper receives the correct values.
Map<String, dynamic> toJson() {
final String firstStartTime =
positions.isNotEmpty ? positions.first.startTime : '00:00';
final String utcOrderDate = toUtcDateIso(orderDate, firstStartTime);
final List<Map<String, dynamic>> positionsList =
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': toUtcTimeHHmm(orderDate, p.startTime),
'endTime': toUtcTimeHHmm(orderDate, p.endTime),
if (p.lunchBreak != null &&
p.lunchBreak != 'NO_BREAK' &&
p.lunchBreak!.isNotEmpty)
'lunchBreakMinutes': breakMinutesFromLabel(p.lunchBreak!),
};
}).toList();
return <String, dynamic>{
'hubId': hubId,
'eventName': eventName,
'orderDate': utcOrderDate,
'positions': positionsList,
if (vendorId != null) 'vendorId': vendorId,
};
}
@override
List<Object?> get props =>
<Object?>[hubId, eventName, orderDate, positions, vendorId];

View File

@@ -63,6 +63,51 @@ class PermanentOrderArguments extends UseCaseArgument {
/// The selected vendor ID, if applicable.
final String? vendorId;
/// Day-of-week labels in Sunday-first order, matching the V2 API convention.
static const List<String> _dayLabels = <String>[
'SUN',
'MON',
'TUE',
'WED',
'THU',
'FRI',
'SAT',
];
/// Serialises these arguments into the V2 API payload shape.
///
/// Times and dates are converted to UTC so the backend's
/// `combineDateAndTime` helper receives the correct values.
Map<String, dynamic> toJson() {
final String firstStartTime =
positions.isNotEmpty ? positions.first.startTime : '00:00';
final String utcStartDate = toUtcDateIso(startDate, firstStartTime);
final List<int> daysOfWeekList = daysOfWeek
.map((String day) => _dayLabels.indexOf(day) % 7)
.toList();
final List<Map<String, dynamic>> positionsList =
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': toUtcTimeHHmm(startDate, p.startTime),
'endTime': toUtcTimeHHmm(startDate, p.endTime),
};
}).toList();
return <String, dynamic>{
'hubId': hubId,
'eventName': eventName,
'startDate': utcStartDate,
'daysOfWeek': daysOfWeekList,
'positions': positionsList,
if (vendorId != null) 'vendorId': vendorId,
};
}
@override
List<Object?> get props => <Object?>[
hubId,

View File

@@ -67,6 +67,53 @@ class RecurringOrderArguments extends UseCaseArgument {
/// The selected vendor ID, if applicable.
final String? vendorId;
/// Day-of-week labels in Sunday-first order, matching the V2 API convention.
static const List<String> _dayLabels = <String>[
'SUN',
'MON',
'TUE',
'WED',
'THU',
'FRI',
'SAT',
];
/// Serialises these arguments into the V2 API payload shape.
///
/// Times and dates are converted to UTC so the backend's
/// `combineDateAndTime` helper receives the correct values.
Map<String, dynamic> toJson() {
final String firstStartTime =
positions.isNotEmpty ? positions.first.startTime : '00:00';
final String utcStartDate = toUtcDateIso(startDate, firstStartTime);
final String utcEndDate = toUtcDateIso(endDate, firstStartTime);
final List<int> recurrenceDaysList = recurringDays
.map((String day) => _dayLabels.indexOf(day) % 7)
.toList();
final List<Map<String, dynamic>> positionsList =
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': toUtcTimeHHmm(startDate, p.startTime),
'endTime': toUtcTimeHHmm(startDate, p.endTime),
};
}).toList();
return <String, dynamic>{
'hubId': hubId,
'eventName': eventName,
'startDate': utcStartDate,
'endDate': utcEndDate,
'recurrenceDays': recurrenceDaysList,
'positions': positionsList,
if (vendorId != null) 'vendorId': vendorId,
};
}
@override
List<Object?> get props => <Object?>[
hubId,

View File

@@ -1,49 +1,19 @@
import 'package:krow_core/core.dart';
import '../arguments/one_time_order_arguments.dart';
import '../repositories/client_create_order_repository_interface.dart';
/// Use case for creating a one-time staffing order.
///
/// 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> {
/// Delegates payload construction to [OneTimeOrderArguments.toJson] and
/// submission to the repository.
class CreateOneTimeOrderUseCase {
/// Creates a [CreateOneTimeOrderUseCase].
const CreateOneTimeOrderUseCase(this._repository);
/// The create-order repository.
final ClientCreateOrderRepositoryInterface _repository;
@override
/// Creates a one-time order from the given arguments.
Future<void> call(OneTimeOrderArguments input) {
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);
return _repository.createOneTimeOrder(input.toJson());
}
}

View File

@@ -1,61 +1,19 @@
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.
///
/// 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> {
/// Delegates payload construction to [PermanentOrderArguments.toJson] and
/// submission to the repository.
class CreatePermanentOrderUseCase {
/// Creates a [CreatePermanentOrderUseCase].
const CreatePermanentOrderUseCase(this._repository);
/// The create-order repository.
final ClientCreateOrderRepositoryInterface _repository;
@override
/// Creates a permanent order from the given arguments.
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);
return _repository.createPermanentOrder(input.toJson());
}
}

View File

@@ -1,63 +1,19 @@
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.
///
/// 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> {
/// Delegates payload construction to [RecurringOrderArguments.toJson] and
/// submission to the repository.
class CreateRecurringOrderUseCase {
/// Creates a [CreateRecurringOrderUseCase].
const CreateRecurringOrderUseCase(this._repository);
/// The create-order repository.
final ClientCreateOrderRepositoryInterface _repository;
@override
/// Creates a recurring order from the given arguments.
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);
return _repository.createRecurringOrder(input.toJson());
}
}

View File

@@ -22,8 +22,8 @@ class ViewOrdersRepositoryImpl implements ViewOrdersRepositoryInterface {
final ApiResponse response = await _api.get(
ClientEndpoints.ordersView,
params: <String, dynamic>{
'startDate': start.toIso8601String(),
'endDate': end.toIso8601String(),
'startDate': start.toUtc().toIso8601String(),
'endDate': end.toUtc().toIso8601String(),
},
);
final Map<String, dynamic> data = response.data as Map<String, dynamic>;

View File

@@ -36,8 +36,8 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
final ApiResponse response = await _apiService.get(
StaffEndpoints.shiftsAssigned,
params: <String, dynamic>{
'startDate': start.toIso8601String(),
'endDate': end.toIso8601String(),
'startDate': start.toUtc().toIso8601String(),
'endDate': end.toUtc().toIso8601String(),
},
);
final List<dynamic> items = _extractItems(response.data);