feat: Add event name to order items and refactor navigation and shift data access to use direct object properties.

This commit is contained in:
Achintha Isuru
2026-02-22 21:07:57 -05:00
parent 0980c6584b
commit 9e38fb7d5f
8 changed files with 150 additions and 93 deletions

View File

@@ -138,7 +138,7 @@ extension ClientNavigator on IModularNavigator {
/// ///
/// This is the starting point for all order creation flows. /// This is the starting point for all order creation flows.
void toCreateOrder({Object? arguments}) { void toCreateOrder({Object? arguments}) {
pushNamed(ClientPaths.createOrder, arguments: arguments); navigate(ClientPaths.createOrder, arguments: arguments);
} }
/// Pushes the rapid order creation flow. /// Pushes the rapid order creation flow.
@@ -175,9 +175,8 @@ extension ClientNavigator on IModularNavigator {
/// Navigates to the order details page to a specific date. /// Navigates to the order details page to a specific date.
void toOrdersSpecificDate(DateTime date) { void toOrdersSpecificDate(DateTime date) {
pushNamedAndRemoveUntil( navigate(
ClientPaths.orders, ClientPaths.orders,
(_) => false,
arguments: <String, DateTime>{'initialDate': date}, arguments: <String, DateTime>{'initialDate': date},
); );
} }

View File

@@ -87,9 +87,10 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
final String orderTypeStr = sr.shift.order.orderType.stringValue final String orderTypeStr = sr.shift.order.orderType.stringValue
.toUpperCase(); .toUpperCase();
final Map<String, dynamic> orderJson = sr.shift.order.toJson(); final dc.ListShiftRolesByVendorIdShiftRolesShiftOrder order =
final DateTime? startDate = _service.toDateTime(orderJson['startDate']); sr.shift.order;
final DateTime? endDate = _service.toDateTime(orderJson['endDate']); final DateTime? startDate = _service.toDateTime(order.startDate);
final DateTime? endDate = _service.toDateTime(order.endDate);
final String startTime = startDt != null final String startTime = startDt != null
? DateFormat('HH:mm').format(startDt) ? DateFormat('HH:mm').format(startDt)
@@ -102,8 +103,8 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
orderType: orderTypeStr, orderType: orderTypeStr,
startDate: startDate, startDate: startDate,
endDate: endDate, endDate: endDate,
recurringDays: sr.shift.order.recurringDays, recurringDays: order.recurringDays,
permanentDays: sr.shift.order.permanentDays, permanentDays: order.permanentDays,
startTime: startTime, startTime: startTime,
endTime: endTime, endTime: endTime,
); );

View File

@@ -23,6 +23,7 @@ class OrderItem extends Equatable {
required this.filled, required this.filled,
required this.workersNeeded, required this.workersNeeded,
required this.hourlyRate, required this.hourlyRate,
required this.eventName,
this.hours = 0, this.hours = 0,
this.totalValue = 0, this.totalValue = 0,
this.confirmedApps = const <Map<String, dynamic>>[], this.confirmedApps = const <Map<String, dynamic>>[],
@@ -76,6 +77,9 @@ class OrderItem extends Equatable {
/// Total value for the shift role. /// Total value for the shift role.
final double totalValue; final double totalValue;
/// Name of the event.
final String eventName;
/// List of confirmed worker applications. /// List of confirmed worker applications.
final List<Map<String, dynamic>> confirmedApps; final List<Map<String, dynamic>> confirmedApps;
@@ -97,6 +101,7 @@ class OrderItem extends Equatable {
hourlyRate, hourlyRate,
hours, hours,
totalValue, totalValue,
eventName,
confirmedApps, confirmedApps,
]; ];
} }

View File

@@ -7,10 +7,8 @@ import '../../domain/repositories/i_view_orders_repository.dart';
/// Implementation of [IViewOrdersRepository] using Data Connect. /// Implementation of [IViewOrdersRepository] using Data Connect.
class ViewOrdersRepositoryImpl implements IViewOrdersRepository { class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
ViewOrdersRepositoryImpl({required dc.DataConnectService service})
ViewOrdersRepositoryImpl({ : _service = service;
required dc.DataConnectService service,
}) : _service = service;
final dc.DataConnectService _service; final dc.DataConnectService _service;
@override @override
@@ -21,34 +19,48 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
return _service.run(() async { return _service.run(() async {
final String businessId = await _service.getBusinessId(); final String businessId = await _service.getBusinessId();
final fdc.Timestamp startTimestamp = _service.toTimestamp(_startOfDay(start)); final fdc.Timestamp startTimestamp = _service.toTimestamp(
_startOfDay(start),
);
final fdc.Timestamp endTimestamp = _service.toTimestamp(_endOfDay(end)); final fdc.Timestamp endTimestamp = _service.toTimestamp(_endOfDay(end));
final fdc.QueryResult<dc.ListShiftRolesByBusinessAndDateRangeData, final fdc.QueryResult<
dc.ListShiftRolesByBusinessAndDateRangeVariables> result = dc.ListShiftRolesByBusinessAndDateRangeData,
await _service.connector dc.ListShiftRolesByBusinessAndDateRangeVariables
.listShiftRolesByBusinessAndDateRange( >
businessId: businessId, result = await _service.connector
start: startTimestamp, .listShiftRolesByBusinessAndDateRange(
end: endTimestamp, businessId: businessId,
) start: startTimestamp,
.execute(); end: endTimestamp,
)
.execute();
debugPrint( debugPrint(
'ViewOrders range start=${start.toIso8601String()} end=${end.toIso8601String()} shiftRoles=${result.data.shiftRoles.length}', 'ViewOrders range start=${start.toIso8601String()} end=${end.toIso8601String()} shiftRoles=${result.data.shiftRoles.length}',
); );
final String businessName = final String businessName =
dc.ClientSessionStore.instance.session?.business?.businessName ?? 'Your Company'; dc.ClientSessionStore.instance.session?.business?.businessName ??
'Your Company';
return result.data.shiftRoles.map((dc.ListShiftRolesByBusinessAndDateRangeShiftRoles shiftRole) { return result.data.shiftRoles.map((
final DateTime? shiftDate = shiftRole.shift.date?.toDateTime().toLocal(); dc.ListShiftRolesByBusinessAndDateRangeShiftRoles shiftRole,
final String dateStr = shiftDate == null ? '' : DateFormat('yyyy-MM-dd').format(shiftDate); ) {
final DateTime? shiftDate = shiftRole.shift.date
?.toDateTime()
.toLocal();
final String dateStr = shiftDate == null
? ''
: DateFormat('yyyy-MM-dd').format(shiftDate);
final String startTime = _formatTime(shiftRole.startTime); final String startTime = _formatTime(shiftRole.startTime);
final String endTime = _formatTime(shiftRole.endTime); final String endTime = _formatTime(shiftRole.endTime);
final int filled = shiftRole.assigned ?? 0; final int filled = shiftRole.assigned ?? 0;
final int workersNeeded = shiftRole.count; final int workersNeeded = shiftRole.count;
final double hours = shiftRole.hours ?? 0; final double hours = shiftRole.hours ?? 0;
final double totalValue = shiftRole.totalValue ?? 0; final double totalValue = shiftRole.totalValue ?? 0;
final double hourlyRate = _hourlyRate(shiftRole.totalValue, shiftRole.hours); final double hourlyRate = _hourlyRate(
shiftRole.totalValue,
shiftRole.hours,
);
// final String status = filled >= workersNeeded ? 'filled' : 'open'; // final String status = filled >= workersNeeded ? 'filled' : 'open';
final String status = shiftRole.shift.status?.stringValue ?? 'OPEN'; final String status = shiftRole.shift.status?.stringValue ?? 'OPEN';
@@ -58,13 +70,17 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
'end=${shiftRole.endTime?.toJson()} hours=$hours totalValue=$totalValue', 'end=${shiftRole.endTime?.toJson()} hours=$hours totalValue=$totalValue',
); );
final String eventName = shiftRole.shift.order.eventName ?? shiftRole.shift.title; final String eventName =
shiftRole.shift.order.eventName ?? shiftRole.shift.title;
return domain.OrderItem( return domain.OrderItem(
id: _shiftRoleKey(shiftRole.shiftId, shiftRole.roleId), id: _shiftRoleKey(shiftRole.shiftId, shiftRole.roleId),
orderId: shiftRole.shift.order.id, orderId: shiftRole.shift.order.id,
orderType: domain.OrderType.fromString(shiftRole.shift.order.orderType.stringValue), orderType: domain.OrderType.fromString(
title: '${shiftRole.role.name} - $eventName', shiftRole.shift.order.orderType.stringValue,
),
title: shiftRole.role.name,
eventName: eventName,
clientName: businessName, clientName: businessName,
status: status, status: status,
date: dateStr, date: dateStr,
@@ -92,28 +108,35 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
final fdc.Timestamp dayStart = _service.toTimestamp(_startOfDay(day)); final fdc.Timestamp dayStart = _service.toTimestamp(_startOfDay(day));
final fdc.Timestamp dayEnd = _service.toTimestamp(_endOfDay(day)); final fdc.Timestamp dayEnd = _service.toTimestamp(_endOfDay(day));
final fdc.QueryResult<dc.ListAcceptedApplicationsByBusinessForDayData, final fdc.QueryResult<
dc.ListAcceptedApplicationsByBusinessForDayVariables> result = dc.ListAcceptedApplicationsByBusinessForDayData,
await _service.connector dc.ListAcceptedApplicationsByBusinessForDayVariables
.listAcceptedApplicationsByBusinessForDay( >
businessId: businessId, result = await _service.connector
dayStart: dayStart, .listAcceptedApplicationsByBusinessForDay(
dayEnd: dayEnd, businessId: businessId,
) dayStart: dayStart,
.execute(); dayEnd: dayEnd,
)
.execute();
print( print(
'ViewOrders day=${day.toIso8601String()} applications=${result.data.applications.length}', 'ViewOrders day=${day.toIso8601String()} applications=${result.data.applications.length}',
); );
final Map<String, List<Map<String, dynamic>>> grouped = <String, List<Map<String, dynamic>>>{}; final Map<String, List<Map<String, dynamic>>> grouped =
for (final dc.ListAcceptedApplicationsByBusinessForDayApplications application <String, List<Map<String, dynamic>>>{};
for (final dc.ListAcceptedApplicationsByBusinessForDayApplications
application
in result.data.applications) { in result.data.applications) {
print( print(
'ViewOrders app: shiftId=${application.shiftId} roleId=${application.roleId} ' 'ViewOrders app: shiftId=${application.shiftId} roleId=${application.roleId} '
'checkIn=${application.checkInTime?.toJson()} checkOut=${application.checkOutTime?.toJson()}', 'checkIn=${application.checkInTime?.toJson()} checkOut=${application.checkOutTime?.toJson()}',
); );
final String key = _shiftRoleKey(application.shiftId, application.roleId); final String key = _shiftRoleKey(
application.shiftId,
application.roleId,
);
grouped.putIfAbsent(key, () => <Map<String, dynamic>>[]); grouped.putIfAbsent(key, () => <Map<String, dynamic>>[]);
grouped[key]!.add(<String, dynamic>{ grouped[key]!.add(<String, dynamic>{
'id': application.id, 'id': application.id,

View File

@@ -85,11 +85,9 @@ class ViewOrdersCubit extends Cubit<ViewOrdersState>
final DateTime? selectedDate = state.selectedDate; final DateTime? selectedDate = state.selectedDate;
final DateTime updatedSelectedDate = final DateTime updatedSelectedDate =
selectedDate != null && selectedDate != null &&
calendarDays.any( calendarDays.any((DateTime day) => _isSameDay(day, selectedDate))
(DateTime day) => _isSameDay(day, selectedDate), ? selectedDate
) : calendarDays.first;
? selectedDate
: calendarDays.first;
emit( emit(
state.copyWith( state.copyWith(
weekOffset: newWeekOffset, weekOffset: newWeekOffset,
@@ -173,13 +171,15 @@ class ViewOrdersCubit extends Cubit<ViewOrdersState>
} }
final int filled = confirmed.length; final int filled = confirmed.length;
final String status = final String status = filled >= order.workersNeeded
filled >= order.workersNeeded ? 'FILLED' : order.status; ? 'FILLED'
: order.status;
return OrderItem( return OrderItem(
id: order.id, id: order.id,
orderId: order.orderId, orderId: order.orderId,
orderType: order.orderType, orderType: order.orderType,
title: order.title, title: order.title,
eventName: order.eventName,
clientName: order.clientName, clientName: order.clientName,
status: status, status: status,
date: order.date, date: order.date,
@@ -224,10 +224,9 @@ class ViewOrdersCubit extends Cubit<ViewOrdersState>
).format(state.selectedDate!); ).format(state.selectedDate!);
// Filter by date // Filter by date
final List<OrderItem> ordersOnDate = final List<OrderItem> ordersOnDate = state.orders
state.orders .where((OrderItem s) => s.date == selectedDateStr)
.where((OrderItem s) => s.date == selectedDateStr) .toList();
.toList();
// Sort by start time // Sort by start time
ordersOnDate.sort( ordersOnDate.sort(
@@ -235,38 +234,35 @@ class ViewOrdersCubit extends Cubit<ViewOrdersState>
); );
if (state.filterTab == 'all') { if (state.filterTab == 'all') {
final List<OrderItem> filtered = final List<OrderItem> filtered = ordersOnDate
ordersOnDate .where(
.where( (OrderItem s) =>
(OrderItem s) => // TODO(orders): move PENDING to its own tab once available.
// TODO(orders): move PENDING to its own tab once available. <String>[
<String>[ 'OPEN',
'OPEN', 'FILLED',
'FILLED', 'CONFIRMED',
'CONFIRMED', 'PENDING',
'PENDING', 'ASSIGNED',
'ASSIGNED', ].contains(s.status),
].contains(s.status), )
) .toList();
.toList();
print( print(
'ViewOrders tab=all statuses=${ordersOnDate.map((OrderItem s) => s.status).toList()} filtered=${filtered.length}', 'ViewOrders tab=all statuses=${ordersOnDate.map((OrderItem s) => s.status).toList()} filtered=${filtered.length}',
); );
return filtered; return filtered;
} else if (state.filterTab == 'active') { } else if (state.filterTab == 'active') {
final List<OrderItem> filtered = final List<OrderItem> filtered = ordersOnDate
ordersOnDate .where((OrderItem s) => s.status == 'IN_PROGRESS')
.where((OrderItem s) => s.status == 'IN_PROGRESS') .toList();
.toList();
print( print(
'ViewOrders tab=active statuses=${ordersOnDate.map((OrderItem s) => s.status).toList()} filtered=${filtered.length}', 'ViewOrders tab=active statuses=${ordersOnDate.map((OrderItem s) => s.status).toList()} filtered=${filtered.length}',
); );
return filtered; return filtered;
} else if (state.filterTab == 'completed') { } else if (state.filterTab == 'completed') {
final List<OrderItem> filtered = final List<OrderItem> filtered = ordersOnDate
ordersOnDate .where((OrderItem s) => s.status == 'COMPLETED')
.where((OrderItem s) => s.status == 'COMPLETED') .toList();
.toList();
print( print(
'ViewOrders tab=completed statuses=${ordersOnDate.map((OrderItem s) => s.status).toList()} filtered=${filtered.length}', 'ViewOrders tab=completed statuses=${ordersOnDate.map((OrderItem s) => s.status).toList()} filtered=${filtered.length}',
); );
@@ -322,4 +318,3 @@ class ViewOrdersCubit extends Cubit<ViewOrdersState>
.length; .length;
} }
} }

View File

@@ -149,6 +149,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
const Divider(height: 1, thickness: 0.5), const Divider(height: 1, thickness: 0.5),
ShiftDateTimeSection( ShiftDateTimeSection(
date: displayShift.date, date: displayShift.date,
endDate: displayShift.endDate,
startTime: displayShift.startTime, startTime: displayShift.startTime,
endTime: displayShift.endTime, endTime: displayShift.endTime,
shiftDateLabel: i18n.shift_date, shiftDateLabel: i18n.shift_date,

View File

@@ -172,13 +172,6 @@ class _MyShiftCardState extends State<MyShiftCard> {
color: UiColors.white, color: UiColors.white,
borderRadius: UiConstants.radiusLg, borderRadius: UiConstants.radiusLg,
border: Border.all(color: UiColors.border), border: Border.all(color: UiColors.border),
boxShadow: [
BoxShadow(
color: UiColors.black.withValues(alpha: 0.05),
blurRadius: 2,
offset: const Offset(0, 1),
),
],
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.all(UiConstants.space4), padding: const EdgeInsets.all(UiConstants.space4),

View File

@@ -7,6 +7,9 @@ class ShiftDateTimeSection extends StatelessWidget {
/// The ISO string of the date. /// The ISO string of the date.
final String date; final String date;
/// The end date string (ISO).
final String? endDate;
/// The start time string (HH:mm). /// The start time string (HH:mm).
final String startTime; final String startTime;
@@ -26,6 +29,7 @@ class ShiftDateTimeSection extends StatelessWidget {
const ShiftDateTimeSection({ const ShiftDateTimeSection({
super.key, super.key,
required this.date, required this.date,
required this.endDate,
required this.startTime, required this.startTime,
required this.endTime, required this.endTime,
required this.shiftDateLabel, required this.shiftDateLabel,
@@ -63,21 +67,57 @@ class ShiftDateTimeSection extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Column(
shiftDateLabel, crossAxisAlignment: CrossAxisAlignment.start,
style: UiTypography.titleUppercase4b.textSecondary,
),
const SizedBox(height: UiConstants.space2),
Row(
children: [ children: [
const Icon(UiIcons.calendar, size: 20, color: UiColors.primary),
const SizedBox(width: UiConstants.space2),
Text( Text(
_formatDate(date), shiftDateLabel,
style: UiTypography.headline5m.textPrimary, style: UiTypography.titleUppercase4b.textSecondary,
),
const SizedBox(height: UiConstants.space2),
Row(
children: [
const Icon(
UiIcons.calendar,
size: 20,
color: UiColors.primary,
),
const SizedBox(width: UiConstants.space2),
Text(
_formatDate(date),
style: UiTypography.headline5m.textPrimary,
),
],
), ),
], ],
), ),
if (endDate != null) ...[
const SizedBox(height: UiConstants.space4),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
shiftDateLabel,
style: UiTypography.titleUppercase4b.textSecondary,
),
const SizedBox(height: UiConstants.space2),
Row(
children: [
const Icon(
UiIcons.calendar,
size: 20,
color: UiColors.primary,
),
const SizedBox(width: UiConstants.space2),
Text(
_formatDate(endDate!),
style: UiTypography.headline5m.textPrimary,
),
],
),
],
),
],
const SizedBox(height: UiConstants.space4), const SizedBox(height: UiConstants.space4),
Row( Row(
children: [ children: [