Merge branch 'origin/dev' into feature/session-persistence-new
This commit is contained in:
@@ -161,6 +161,7 @@ class _CoverageShiftListState extends State<CoverageShiftList> {
|
||||
children: <Widget>[
|
||||
ShiftHeader(
|
||||
title: shift.roleName,
|
||||
locationName: shift.locationName,
|
||||
startTime: _formatTime(shift.timeRange.startsAt),
|
||||
current: shift.assignedWorkerCount,
|
||||
total: shift.requiredWorkerCount,
|
||||
@@ -226,9 +227,10 @@ class _CoverageShiftListState extends State<CoverageShiftList> {
|
||||
worker: worker,
|
||||
shiftStartTime: _formatTime(shift.timeRange.startsAt),
|
||||
showRateButton:
|
||||
worker.status == AssignmentStatus.checkedIn ||
|
||||
worker.status == AssignmentStatus.checkedOut ||
|
||||
worker.status == AssignmentStatus.completed,
|
||||
!worker.hasReview &&
|
||||
(worker.status == AssignmentStatus.checkedIn ||
|
||||
worker.status == AssignmentStatus.checkedOut ||
|
||||
worker.status == AssignmentStatus.completed),
|
||||
showCancelButton:
|
||||
DateTime.now().isAfter(shift.timeRange.startsAt) &&
|
||||
(worker.status == AssignmentStatus.noShow ||
|
||||
|
||||
@@ -21,6 +21,7 @@ class ShiftHeader extends StatelessWidget {
|
||||
required this.lateCount,
|
||||
required this.isExpanded,
|
||||
required this.onToggle,
|
||||
this.locationName,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@@ -57,6 +58,9 @@ class ShiftHeader extends StatelessWidget {
|
||||
/// Callback invoked when the header is tapped to expand or collapse.
|
||||
final VoidCallback onToggle;
|
||||
|
||||
/// Optional location or hub name for the shift.
|
||||
final String? locationName;
|
||||
|
||||
/// Returns the status colour based on [coveragePercent].
|
||||
///
|
||||
/// Green for >= 100 %, yellow for >= 80 %, red otherwise.
|
||||
@@ -110,6 +114,29 @@ class ShiftHeader extends StatelessWidget {
|
||||
title,
|
||||
style: UiTypography.body1b.textPrimary,
|
||||
),
|
||||
if (locationName != null &&
|
||||
locationName!.isNotEmpty) ...<Widget>[
|
||||
const SizedBox(height: 2),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
const Icon(
|
||||
UiIcons.mapPin,
|
||||
size: 10,
|
||||
color: UiColors.textSecondary,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Expanded(
|
||||
child: Text(
|
||||
locationName!,
|
||||
style: UiTypography.body3r.copyWith(
|
||||
color: UiColors.textSecondary,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
const SizedBox(height: UiConstants.space1),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
|
||||
@@ -75,7 +75,7 @@ class ReorderWidget extends StatelessWidget {
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
),
|
||||
child: const Icon(
|
||||
UiIcons.building,
|
||||
UiIcons.briefcase,
|
||||
size: 16,
|
||||
color: UiColors.primary,
|
||||
),
|
||||
@@ -104,18 +104,6 @@ class ReorderWidget extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
// Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.end,
|
||||
// children: <Widget>[
|
||||
// // ASSUMPTION: No i18n key for 'positions' under
|
||||
// // reorder section — carrying forward existing
|
||||
// // hardcoded string pattern for this migration.
|
||||
// Text(
|
||||
// '${order.positionCount} positions',
|
||||
// style: UiTypography.footnote2r.textSecondary,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: UiConstants.space3),
|
||||
@@ -130,7 +118,7 @@ class ReorderWidget extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(width: UiConstants.space2),
|
||||
_Badge(
|
||||
icon: UiIcons.building,
|
||||
icon: UiIcons.users,
|
||||
text: '${order.positionCount}',
|
||||
color: UiColors.textSecondary,
|
||||
bg: UiColors.buttonSecondaryStill,
|
||||
|
||||
@@ -10,6 +10,7 @@ class OneTimeOrderPositionArgument extends UseCaseArgument {
|
||||
required this.endTime,
|
||||
this.roleName,
|
||||
this.lunchBreak,
|
||||
this.hourlyRateCents,
|
||||
});
|
||||
|
||||
/// The role ID for this position.
|
||||
@@ -30,9 +31,19 @@ class OneTimeOrderPositionArgument extends UseCaseArgument {
|
||||
/// Break duration label (e.g. `'MIN_30'`, `'NO_BREAK'`), if set.
|
||||
final String? lunchBreak;
|
||||
|
||||
/// Hourly rate in cents for this position, if set.
|
||||
final int? hourlyRateCents;
|
||||
|
||||
@override
|
||||
List<Object?> get props =>
|
||||
<Object?>[roleId, roleName, workerCount, startTime, endTime, lunchBreak];
|
||||
List<Object?> get props => <Object?>[
|
||||
roleId,
|
||||
roleName,
|
||||
workerCount,
|
||||
startTime,
|
||||
endTime,
|
||||
lunchBreak,
|
||||
hourlyRateCents,
|
||||
];
|
||||
}
|
||||
|
||||
/// Typed arguments for [CreateOneTimeOrderUseCase].
|
||||
@@ -63,6 +74,40 @@ 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!),
|
||||
if (p.hourlyRateCents != null) 'hourlyRateCents': p.hourlyRateCents,
|
||||
};
|
||||
}).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];
|
||||
|
||||
@@ -9,6 +9,7 @@ class PermanentOrderPositionArgument extends UseCaseArgument {
|
||||
required this.startTime,
|
||||
required this.endTime,
|
||||
this.roleName,
|
||||
this.hourlyRateCents,
|
||||
});
|
||||
|
||||
/// The role ID for this position.
|
||||
@@ -26,9 +27,18 @@ class PermanentOrderPositionArgument extends UseCaseArgument {
|
||||
/// Shift end time in HH:mm format.
|
||||
final String endTime;
|
||||
|
||||
/// Hourly rate in cents for this position, if set.
|
||||
final int? hourlyRateCents;
|
||||
|
||||
@override
|
||||
List<Object?> get props =>
|
||||
<Object?>[roleId, roleName, workerCount, startTime, endTime];
|
||||
List<Object?> get props => <Object?>[
|
||||
roleId,
|
||||
roleName,
|
||||
workerCount,
|
||||
startTime,
|
||||
endTime,
|
||||
hourlyRateCents,
|
||||
];
|
||||
}
|
||||
|
||||
/// Typed arguments for [CreatePermanentOrderUseCase].
|
||||
@@ -63,6 +73,52 @@ 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),
|
||||
if (p.hourlyRateCents != null) 'hourlyRateCents': p.hourlyRateCents,
|
||||
};
|
||||
}).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,
|
||||
|
||||
@@ -9,6 +9,7 @@ class RecurringOrderPositionArgument extends UseCaseArgument {
|
||||
required this.startTime,
|
||||
required this.endTime,
|
||||
this.roleName,
|
||||
this.hourlyRateCents,
|
||||
});
|
||||
|
||||
/// The role ID for this position.
|
||||
@@ -26,9 +27,18 @@ class RecurringOrderPositionArgument extends UseCaseArgument {
|
||||
/// Shift end time in HH:mm format.
|
||||
final String endTime;
|
||||
|
||||
/// Hourly rate in cents for this position, if set.
|
||||
final int? hourlyRateCents;
|
||||
|
||||
@override
|
||||
List<Object?> get props =>
|
||||
<Object?>[roleId, roleName, workerCount, startTime, endTime];
|
||||
List<Object?> get props => <Object?>[
|
||||
roleId,
|
||||
roleName,
|
||||
workerCount,
|
||||
startTime,
|
||||
endTime,
|
||||
hourlyRateCents,
|
||||
];
|
||||
}
|
||||
|
||||
/// Typed arguments for [CreateRecurringOrderUseCase].
|
||||
@@ -67,6 +77,54 @@ 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),
|
||||
if (p.hourlyRateCents != null) 'hourlyRateCents': p.hourlyRateCents,
|
||||
};
|
||||
}).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,
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,6 +265,8 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState>
|
||||
startTime: p.startTime,
|
||||
endTime: p.endTime,
|
||||
lunchBreak: p.lunchBreak,
|
||||
hourlyRateCents:
|
||||
role != null ? (role.costPerHour * 100).round() : null,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
|
||||
@@ -360,6 +360,8 @@ class PermanentOrderBloc extends Bloc<PermanentOrderEvent, PermanentOrderState>
|
||||
workerCount: p.count,
|
||||
startTime: p.startTime,
|
||||
endTime: p.endTime,
|
||||
hourlyRateCents:
|
||||
role != null ? (role.costPerHour * 100).round() : null,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
|
||||
@@ -380,6 +380,8 @@ class RecurringOrderBloc extends Bloc<RecurringOrderEvent, RecurringOrderState>
|
||||
workerCount: p.count,
|
||||
startTime: p.startTime,
|
||||
endTime: p.endTime,
|
||||
hourlyRateCents:
|
||||
role != null ? (role.costPerHour * 100).round() : null,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -48,13 +48,13 @@ class OrderEditSheetState extends State<OrderEditSheet> {
|
||||
_orderNameController = TextEditingController(text: widget.order.roleName);
|
||||
|
||||
final String startHH =
|
||||
widget.order.startsAt.toLocal().hour.toString().padLeft(2, '0');
|
||||
widget.order.startsAt.hour.toString().padLeft(2, '0');
|
||||
final String startMM =
|
||||
widget.order.startsAt.toLocal().minute.toString().padLeft(2, '0');
|
||||
widget.order.startsAt.minute.toString().padLeft(2, '0');
|
||||
final String endHH =
|
||||
widget.order.endsAt.toLocal().hour.toString().padLeft(2, '0');
|
||||
widget.order.endsAt.hour.toString().padLeft(2, '0');
|
||||
final String endMM =
|
||||
widget.order.endsAt.toLocal().minute.toString().padLeft(2, '0');
|
||||
widget.order.endsAt.minute.toString().padLeft(2, '0');
|
||||
|
||||
_positions = <Map<String, dynamic>>[
|
||||
<String, dynamic>{
|
||||
|
||||
@@ -77,9 +77,8 @@ class _ViewOrderCardState extends State<ViewOrderCard> {
|
||||
|
||||
/// Formats a [DateTime] to a display time string (e.g. "9:00 AM").
|
||||
String _formatTime({required DateTime dateTime}) {
|
||||
final DateTime local = dateTime.toLocal();
|
||||
final int hour24 = local.hour;
|
||||
final int minute = local.minute;
|
||||
final int hour24 = dateTime.hour;
|
||||
final int minute = dateTime.minute;
|
||||
final String ampm = hour24 >= 12 ? 'PM' : 'AM';
|
||||
int hour = hour24 % 12;
|
||||
if (hour == 0) hour = 12;
|
||||
@@ -124,7 +123,9 @@ class _ViewOrderCardState extends State<ViewOrderCard> {
|
||||
: 0;
|
||||
|
||||
final double hours = _computeHours(order);
|
||||
final double cost = order.totalCostCents / 100.0;
|
||||
final double cost = order.totalValue > 0
|
||||
? order.totalValue
|
||||
: order.totalCostCents / 100.0;
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
|
||||
Reference in New Issue
Block a user