refactor: Update reorder suggestions to fetch and display completed orders with aggregated totals instead of individual shift roles.

This commit is contained in:
Achintha Isuru
2026-02-21 22:44:26 -05:00
parent 2c6cd9cd45
commit c5e48ffbc6
4 changed files with 160 additions and 33 deletions

View File

@@ -1,46 +1,51 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
/// Summary of a completed shift role used for reorder suggestions. /// Summary of a completed order used for reorder suggestions.
class ReorderItem extends Equatable { class ReorderItem extends Equatable {
const ReorderItem({ const ReorderItem({
required this.orderId, required this.orderId,
required this.title, required this.title,
required this.location, required this.location,
required this.hourlyRate, required this.totalCost,
required this.hours,
required this.workers, required this.workers,
required this.type, required this.type,
this.hourlyRate = 0,
this.hours = 0,
}); });
/// Parent order id for the completed shift. /// Unique identifier of the order.
final String orderId; final String orderId;
/// Display title (role + shift title). /// Display title of the order (e.g., event name or first shift title).
final String title; final String title;
/// Location from the shift. /// Location of the order (e.g., first shift location).
final String location; final String location;
/// Hourly rate from the role. /// Total calculated cost for the order.
final double hourlyRate; final double totalCost;
/// Total hours for the shift role. /// Total number of workers required for the order.
final double hours;
/// Worker count for the shift role.
final int workers; final int workers;
/// Order type (e.g., ONE_TIME). /// The type of order (e.g., ONE_TIME, RECURRING).
final String type; final String type;
/// Average or primary hourly rate (optional, for display).
final double hourlyRate;
/// Total hours for the order (optional, for display).
final double hours;
@override @override
List<Object?> get props => <Object?>[ List<Object?> get props => <Object?>[
orderId, orderId,
title, title,
location, location,
hourlyRate, totalCost,
hours,
workers, workers,
type, type,
hourlyRate,
hours,
]; ];
} }

View File

@@ -148,31 +148,59 @@ class HomeRepositoryImpl implements HomeRepositoryInterface {
final DateTime start = now.subtract(const Duration(days: 30)); final DateTime start = now.subtract(const Duration(days: 30));
final QueryResult< final QueryResult<
dc.ListShiftRolesByBusinessDateRangeCompletedOrdersData, dc.ListCompletedOrdersByBusinessAndDateRangeData,
dc.ListShiftRolesByBusinessDateRangeCompletedOrdersVariables dc.ListCompletedOrdersByBusinessAndDateRangeVariables
> >
result = await _service.connector result = await _service.connector
.listShiftRolesByBusinessDateRangeCompletedOrders( .listCompletedOrdersByBusinessAndDateRange(
businessId: businessId, businessId: businessId,
start: _service.toTimestamp(start), start: _service.toTimestamp(start),
end: _service.toTimestamp(now), end: _service.toTimestamp(now),
) )
.execute(); .execute();
return result.data.shiftRoles.map(( return result.data.orders.map((
dc.ListShiftRolesByBusinessDateRangeCompletedOrdersShiftRoles shiftRole, dc.ListCompletedOrdersByBusinessAndDateRangeOrders order,
) { ) {
final String location = final String title =
shiftRole.shift.location ?? shiftRole.shift.locationAddress ?? ''; order.eventName ??
final String type = shiftRole.shift.order.orderType.stringValue; (order.shifts_on_order.isNotEmpty
? order.shifts_on_order[0].title
: 'Order');
final String location = order.shifts_on_order.isNotEmpty
? (order.shifts_on_order[0].location ??
order.shifts_on_order[0].locationAddress ??
'')
: '';
int totalWorkers = 0;
double totalHours = 0;
double totalRate = 0;
int roleCount = 0;
for (final dc.ListCompletedOrdersByBusinessAndDateRangeOrdersShiftsOnOrder
shift
in order.shifts_on_order) {
for (final dc.ListCompletedOrdersByBusinessAndDateRangeOrdersShiftsOnOrderShiftRolesOnShift
role
in shift.shiftRoles_on_shift) {
totalWorkers += role.count;
totalHours += role.hours ?? 0;
totalRate += role.role.costPerHour;
roleCount++;
}
}
return ReorderItem( return ReorderItem(
orderId: shiftRole.shift.order.id, orderId: order.id,
title: '${shiftRole.role.name} - ${shiftRole.shift.title}', title: title,
location: location, location: location,
hourlyRate: shiftRole.role.costPerHour, totalCost: order.total ?? 0.0,
hours: shiftRole.hours ?? 0, workers: totalWorkers,
workers: shiftRole.count, type: order.orderType.stringValue,
type: type, hourlyRate: roleCount > 0 ? totalRate / roleCount : 0.0,
hours: totalHours,
); );
}).toList(); }).toList();
}); });

View File

@@ -5,7 +5,6 @@ import 'package:krow_domain/krow_domain.dart';
/// A widget that allows clients to reorder recent shifts. /// A widget that allows clients to reorder recent shifts.
class ReorderWidget extends StatelessWidget { class ReorderWidget extends StatelessWidget {
/// Creates a [ReorderWidget]. /// Creates a [ReorderWidget].
const ReorderWidget({ const ReorderWidget({
super.key, super.key,
@@ -13,6 +12,7 @@ class ReorderWidget extends StatelessWidget {
required this.onReorderPressed, required this.onReorderPressed,
this.subtitle, this.subtitle,
}); });
/// Recent completed orders for reorder. /// Recent completed orders for reorder.
final List<ReorderItem> orders; final List<ReorderItem> orders;
@@ -55,8 +55,7 @@ class ReorderWidget extends StatelessWidget {
const SizedBox(width: UiConstants.space3), const SizedBox(width: UiConstants.space3),
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
final ReorderItem order = recentOrders[index]; final ReorderItem order = recentOrders[index];
final double totalCost = final double totalCost = order.totalCost;
order.hourlyRate * order.hours * order.workers;
return Container( return Container(
width: 260, width: 260,
@@ -163,6 +162,7 @@ class ReorderWidget extends StatelessWidget {
'hours': order.hours, 'hours': order.hours,
'workers': order.workers, 'workers': order.workers,
'type': order.type, 'type': order.type,
'totalCost': order.totalCost,
}), }),
), ),
], ],
@@ -177,7 +177,6 @@ class ReorderWidget extends StatelessWidget {
} }
class _Badge extends StatelessWidget { class _Badge extends StatelessWidget {
const _Badge({ const _Badge({
required this.icon, required this.icon,
required this.text, required this.text,

View File

@@ -433,3 +433,98 @@ query listOrdersByBusinessAndTeamHub(
createdBy createdBy
} }
} }
# ------------------------------------------------------------
# GET COMPLETED ORDERS BY BUSINESS AND DATE RANGE
# ------------------------------------------------------------
query listCompletedOrdersByBusinessAndDateRange(
$businessId: UUID!
$start: Timestamp!
$end: Timestamp!
$offset: Int
$limit: Int
) @auth(level: USER) {
orders(
where: {
businessId: { eq: $businessId }
status: { eq: COMPLETED }
date: { ge: $start, le: $end }
}
offset: $offset
limit: $limit
orderBy: { createdAt: DESC }
) {
id
eventName
vendorId
businessId
orderType
status
date
startDate
endDate
duration
lunchBreak
total
assignedStaff
requested
recurringDays
permanentDays
poReference
notes
createdAt
business {
id
businessName
email
contactName
}
vendor {
id
companyName
}
teamHub {
address
placeId
hubName
}
# Assigned shifts and their roles
shifts_on_order {
id
title
date
startTime
endTime
hours
cost
location
locationAddress
status
workersNeeded
filled
shiftRoles_on_shift {
id
roleId
count
assigned
startTime
endTime
hours
totalValue
role {
id
name
costPerHour
}
}
}
}
}