From 9e38fb7d5ff592d9065c82722dc3f3bea0e9112f Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Sun, 22 Feb 2026 21:07:57 -0500 Subject: [PATCH] feat: Add event name to order items and refactor navigation and shift data access to use direct object properties. --- .../lib/src/routing/client/navigator.dart | 5 +- .../shifts_connector_repository_impl.dart | 11 ++- .../lib/src/entities/orders/order_item.dart | 5 + .../view_orders_repository_impl.dart | 91 ++++++++++++------- .../presentation/blocs/view_orders_cubit.dart | 63 ++++++------- .../pages/shift_details_page.dart | 1 + .../presentation/widgets/my_shift_card.dart | 7 -- .../shift_date_time_section.dart | 60 ++++++++++-- 8 files changed, 150 insertions(+), 93 deletions(-) diff --git a/apps/mobile/packages/core/lib/src/routing/client/navigator.dart b/apps/mobile/packages/core/lib/src/routing/client/navigator.dart index f969af72..1c3c7c6e 100644 --- a/apps/mobile/packages/core/lib/src/routing/client/navigator.dart +++ b/apps/mobile/packages/core/lib/src/routing/client/navigator.dart @@ -138,7 +138,7 @@ extension ClientNavigator on IModularNavigator { /// /// This is the starting point for all order creation flows. void toCreateOrder({Object? arguments}) { - pushNamed(ClientPaths.createOrder, arguments: arguments); + navigate(ClientPaths.createOrder, arguments: arguments); } /// Pushes the rapid order creation flow. @@ -175,9 +175,8 @@ extension ClientNavigator on IModularNavigator { /// Navigates to the order details page to a specific date. void toOrdersSpecificDate(DateTime date) { - pushNamedAndRemoveUntil( + navigate( ClientPaths.orders, - (_) => false, arguments: {'initialDate': date}, ); } diff --git a/apps/mobile/packages/data_connect/lib/src/connectors/shifts/data/repositories/shifts_connector_repository_impl.dart b/apps/mobile/packages/data_connect/lib/src/connectors/shifts/data/repositories/shifts_connector_repository_impl.dart index 02c89528..cb760a6f 100644 --- a/apps/mobile/packages/data_connect/lib/src/connectors/shifts/data/repositories/shifts_connector_repository_impl.dart +++ b/apps/mobile/packages/data_connect/lib/src/connectors/shifts/data/repositories/shifts_connector_repository_impl.dart @@ -87,9 +87,10 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository { final String orderTypeStr = sr.shift.order.orderType.stringValue .toUpperCase(); - final Map orderJson = sr.shift.order.toJson(); - final DateTime? startDate = _service.toDateTime(orderJson['startDate']); - final DateTime? endDate = _service.toDateTime(orderJson['endDate']); + final dc.ListShiftRolesByVendorIdShiftRolesShiftOrder order = + sr.shift.order; + final DateTime? startDate = _service.toDateTime(order.startDate); + final DateTime? endDate = _service.toDateTime(order.endDate); final String startTime = startDt != null ? DateFormat('HH:mm').format(startDt) @@ -102,8 +103,8 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository { orderType: orderTypeStr, startDate: startDate, endDate: endDate, - recurringDays: sr.shift.order.recurringDays, - permanentDays: sr.shift.order.permanentDays, + recurringDays: order.recurringDays, + permanentDays: order.permanentDays, startTime: startTime, endTime: endTime, ); diff --git a/apps/mobile/packages/domain/lib/src/entities/orders/order_item.dart b/apps/mobile/packages/domain/lib/src/entities/orders/order_item.dart index 0a9d0d69..b9ab956f 100644 --- a/apps/mobile/packages/domain/lib/src/entities/orders/order_item.dart +++ b/apps/mobile/packages/domain/lib/src/entities/orders/order_item.dart @@ -23,6 +23,7 @@ class OrderItem extends Equatable { required this.filled, required this.workersNeeded, required this.hourlyRate, + required this.eventName, this.hours = 0, this.totalValue = 0, this.confirmedApps = const >[], @@ -76,6 +77,9 @@ class OrderItem extends Equatable { /// Total value for the shift role. final double totalValue; + /// Name of the event. + final String eventName; + /// List of confirmed worker applications. final List> confirmedApps; @@ -97,6 +101,7 @@ class OrderItem extends Equatable { hourlyRate, hours, totalValue, + eventName, confirmedApps, ]; } diff --git a/apps/mobile/packages/features/client/orders/view_orders/lib/src/data/repositories/view_orders_repository_impl.dart b/apps/mobile/packages/features/client/orders/view_orders/lib/src/data/repositories/view_orders_repository_impl.dart index 99068d25..e0e79a28 100644 --- a/apps/mobile/packages/features/client/orders/view_orders/lib/src/data/repositories/view_orders_repository_impl.dart +++ b/apps/mobile/packages/features/client/orders/view_orders/lib/src/data/repositories/view_orders_repository_impl.dart @@ -7,10 +7,8 @@ import '../../domain/repositories/i_view_orders_repository.dart'; /// Implementation of [IViewOrdersRepository] using Data Connect. class ViewOrdersRepositoryImpl implements IViewOrdersRepository { - - ViewOrdersRepositoryImpl({ - required dc.DataConnectService service, - }) : _service = service; + ViewOrdersRepositoryImpl({required dc.DataConnectService service}) + : _service = service; final dc.DataConnectService _service; @override @@ -21,34 +19,48 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository { return _service.run(() async { 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.QueryResult result = - await _service.connector - .listShiftRolesByBusinessAndDateRange( - businessId: businessId, - start: startTimestamp, - end: endTimestamp, - ) - .execute(); + final fdc.QueryResult< + dc.ListShiftRolesByBusinessAndDateRangeData, + dc.ListShiftRolesByBusinessAndDateRangeVariables + > + result = await _service.connector + .listShiftRolesByBusinessAndDateRange( + businessId: businessId, + start: startTimestamp, + end: endTimestamp, + ) + .execute(); debugPrint( 'ViewOrders range start=${start.toIso8601String()} end=${end.toIso8601String()} shiftRoles=${result.data.shiftRoles.length}', ); 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) { - final DateTime? shiftDate = shiftRole.shift.date?.toDateTime().toLocal(); - final String dateStr = shiftDate == null ? '' : DateFormat('yyyy-MM-dd').format(shiftDate); + return result.data.shiftRoles.map(( + dc.ListShiftRolesByBusinessAndDateRangeShiftRoles shiftRole, + ) { + 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 endTime = _formatTime(shiftRole.endTime); final int filled = shiftRole.assigned ?? 0; final int workersNeeded = shiftRole.count; final double hours = shiftRole.hours ?? 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 = shiftRole.shift.status?.stringValue ?? 'OPEN'; @@ -58,13 +70,17 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository { '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( id: _shiftRoleKey(shiftRole.shiftId, shiftRole.roleId), orderId: shiftRole.shift.order.id, - orderType: domain.OrderType.fromString(shiftRole.shift.order.orderType.stringValue), - title: '${shiftRole.role.name} - $eventName', + orderType: domain.OrderType.fromString( + shiftRole.shift.order.orderType.stringValue, + ), + title: shiftRole.role.name, + eventName: eventName, clientName: businessName, status: status, date: dateStr, @@ -92,28 +108,35 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository { final fdc.Timestamp dayStart = _service.toTimestamp(_startOfDay(day)); final fdc.Timestamp dayEnd = _service.toTimestamp(_endOfDay(day)); - final fdc.QueryResult result = - await _service.connector - .listAcceptedApplicationsByBusinessForDay( - businessId: businessId, - dayStart: dayStart, - dayEnd: dayEnd, - ) - .execute(); + final fdc.QueryResult< + dc.ListAcceptedApplicationsByBusinessForDayData, + dc.ListAcceptedApplicationsByBusinessForDayVariables + > + result = await _service.connector + .listAcceptedApplicationsByBusinessForDay( + businessId: businessId, + dayStart: dayStart, + dayEnd: dayEnd, + ) + .execute(); print( 'ViewOrders day=${day.toIso8601String()} applications=${result.data.applications.length}', ); - final Map>> grouped = >>{}; - for (final dc.ListAcceptedApplicationsByBusinessForDayApplications application + final Map>> grouped = + >>{}; + for (final dc.ListAcceptedApplicationsByBusinessForDayApplications + application in result.data.applications) { print( 'ViewOrders app: shiftId=${application.shiftId} roleId=${application.roleId} ' '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, () => >[]); grouped[key]!.add({ 'id': application.id, diff --git a/apps/mobile/packages/features/client/orders/view_orders/lib/src/presentation/blocs/view_orders_cubit.dart b/apps/mobile/packages/features/client/orders/view_orders/lib/src/presentation/blocs/view_orders_cubit.dart index 697cca50..3ea97a5e 100644 --- a/apps/mobile/packages/features/client/orders/view_orders/lib/src/presentation/blocs/view_orders_cubit.dart +++ b/apps/mobile/packages/features/client/orders/view_orders/lib/src/presentation/blocs/view_orders_cubit.dart @@ -85,11 +85,9 @@ class ViewOrdersCubit extends Cubit final DateTime? selectedDate = state.selectedDate; final DateTime updatedSelectedDate = selectedDate != null && - calendarDays.any( - (DateTime day) => _isSameDay(day, selectedDate), - ) - ? selectedDate - : calendarDays.first; + calendarDays.any((DateTime day) => _isSameDay(day, selectedDate)) + ? selectedDate + : calendarDays.first; emit( state.copyWith( weekOffset: newWeekOffset, @@ -173,13 +171,15 @@ class ViewOrdersCubit extends Cubit } final int filled = confirmed.length; - final String status = - filled >= order.workersNeeded ? 'FILLED' : order.status; + final String status = filled >= order.workersNeeded + ? 'FILLED' + : order.status; return OrderItem( id: order.id, orderId: order.orderId, orderType: order.orderType, title: order.title, + eventName: order.eventName, clientName: order.clientName, status: status, date: order.date, @@ -224,10 +224,9 @@ class ViewOrdersCubit extends Cubit ).format(state.selectedDate!); // Filter by date - final List ordersOnDate = - state.orders - .where((OrderItem s) => s.date == selectedDateStr) - .toList(); + final List ordersOnDate = state.orders + .where((OrderItem s) => s.date == selectedDateStr) + .toList(); // Sort by start time ordersOnDate.sort( @@ -235,38 +234,35 @@ class ViewOrdersCubit extends Cubit ); if (state.filterTab == 'all') { - final List filtered = - ordersOnDate - .where( - (OrderItem s) => - // TODO(orders): move PENDING to its own tab once available. - [ - 'OPEN', - 'FILLED', - 'CONFIRMED', - 'PENDING', - 'ASSIGNED', - ].contains(s.status), - ) - .toList(); + final List filtered = ordersOnDate + .where( + (OrderItem s) => + // TODO(orders): move PENDING to its own tab once available. + [ + 'OPEN', + 'FILLED', + 'CONFIRMED', + 'PENDING', + 'ASSIGNED', + ].contains(s.status), + ) + .toList(); print( 'ViewOrders tab=all statuses=${ordersOnDate.map((OrderItem s) => s.status).toList()} filtered=${filtered.length}', ); return filtered; } else if (state.filterTab == 'active') { - final List filtered = - ordersOnDate - .where((OrderItem s) => s.status == 'IN_PROGRESS') - .toList(); + final List filtered = ordersOnDate + .where((OrderItem s) => s.status == 'IN_PROGRESS') + .toList(); print( 'ViewOrders tab=active statuses=${ordersOnDate.map((OrderItem s) => s.status).toList()} filtered=${filtered.length}', ); return filtered; } else if (state.filterTab == 'completed') { - final List filtered = - ordersOnDate - .where((OrderItem s) => s.status == 'COMPLETED') - .toList(); + final List filtered = ordersOnDate + .where((OrderItem s) => s.status == 'COMPLETED') + .toList(); print( 'ViewOrders tab=completed statuses=${ordersOnDate.map((OrderItem s) => s.status).toList()} filtered=${filtered.length}', ); @@ -322,4 +318,3 @@ class ViewOrdersCubit extends Cubit .length; } } - diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/pages/shift_details_page.dart b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/pages/shift_details_page.dart index ea532d7a..fcb8f22a 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/pages/shift_details_page.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/pages/shift_details_page.dart @@ -149,6 +149,7 @@ class _ShiftDetailsPageState extends State { const Divider(height: 1, thickness: 0.5), ShiftDateTimeSection( date: displayShift.date, + endDate: displayShift.endDate, startTime: displayShift.startTime, endTime: displayShift.endTime, shiftDateLabel: i18n.shift_date, diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/widgets/my_shift_card.dart b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/widgets/my_shift_card.dart index 36f59053..ee2944e0 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/widgets/my_shift_card.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/widgets/my_shift_card.dart @@ -172,13 +172,6 @@ class _MyShiftCardState extends State { color: UiColors.white, borderRadius: UiConstants.radiusLg, border: Border.all(color: UiColors.border), - boxShadow: [ - BoxShadow( - color: UiColors.black.withValues(alpha: 0.05), - blurRadius: 2, - offset: const Offset(0, 1), - ), - ], ), child: Padding( padding: const EdgeInsets.all(UiConstants.space4), diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/widgets/shift_details/shift_date_time_section.dart b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/widgets/shift_details/shift_date_time_section.dart index b4b7c07f..76dec5f5 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/widgets/shift_details/shift_date_time_section.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/widgets/shift_details/shift_date_time_section.dart @@ -7,6 +7,9 @@ class ShiftDateTimeSection extends StatelessWidget { /// The ISO string of the date. final String date; + /// The end date string (ISO). + final String? endDate; + /// The start time string (HH:mm). final String startTime; @@ -26,6 +29,7 @@ class ShiftDateTimeSection extends StatelessWidget { const ShiftDateTimeSection({ super.key, required this.date, + required this.endDate, required this.startTime, required this.endTime, required this.shiftDateLabel, @@ -63,21 +67,57 @@ class ShiftDateTimeSection extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - shiftDateLabel, - style: UiTypography.titleUppercase4b.textSecondary, - ), - const SizedBox(height: UiConstants.space2), - Row( + Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Icon(UiIcons.calendar, size: 20, color: UiColors.primary), - const SizedBox(width: UiConstants.space2), Text( - _formatDate(date), - style: UiTypography.headline5m.textPrimary, + 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(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), Row( children: [