refactor: Update reorder suggestions to fetch and display completed orders with aggregated totals instead of individual shift roles.
This commit is contained in:
@@ -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,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user