feat: Add orderId and normalized orderType to the Shift model to enable UI grouping and type-badging in shift displays.
This commit is contained in:
@@ -10,9 +10,8 @@ import '../../domain/repositories/shifts_connector_repository.dart';
|
|||||||
/// Handles shift-related data operations by interacting with Data Connect.
|
/// Handles shift-related data operations by interacting with Data Connect.
|
||||||
class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
||||||
/// Creates a new [ShiftsConnectorRepositoryImpl].
|
/// Creates a new [ShiftsConnectorRepositoryImpl].
|
||||||
ShiftsConnectorRepositoryImpl({
|
ShiftsConnectorRepositoryImpl({dc.DataConnectService? service})
|
||||||
dc.DataConnectService? service,
|
: _service = service ?? dc.DataConnectService.instance;
|
||||||
}) : _service = service ?? dc.DataConnectService.instance;
|
|
||||||
|
|
||||||
final dc.DataConnectService _service;
|
final dc.DataConnectService _service;
|
||||||
|
|
||||||
@@ -23,12 +22,17 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
required DateTime end,
|
required DateTime end,
|
||||||
}) async {
|
}) async {
|
||||||
return _service.run(() async {
|
return _service.run(() async {
|
||||||
final dc.GetApplicationsByStaffIdVariablesBuilder query = _service.connector
|
final dc.GetApplicationsByStaffIdVariablesBuilder query = _service
|
||||||
|
.connector
|
||||||
.getApplicationsByStaffId(staffId: staffId)
|
.getApplicationsByStaffId(staffId: staffId)
|
||||||
.dayStart(_service.toTimestamp(start))
|
.dayStart(_service.toTimestamp(start))
|
||||||
.dayEnd(_service.toTimestamp(end));
|
.dayEnd(_service.toTimestamp(end));
|
||||||
|
|
||||||
final QueryResult<dc.GetApplicationsByStaffIdData, dc.GetApplicationsByStaffIdVariables> response = await query.execute();
|
final QueryResult<
|
||||||
|
dc.GetApplicationsByStaffIdData,
|
||||||
|
dc.GetApplicationsByStaffIdVariables
|
||||||
|
>
|
||||||
|
response = await query.execute();
|
||||||
return _mapApplicationsToShifts(response.data.applications);
|
return _mapApplicationsToShifts(response.data.applications);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -45,18 +49,28 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
final String? vendorId = dc.StaffSessionStore.instance.session?.ownerId;
|
final String? vendorId = dc.StaffSessionStore.instance.session?.ownerId;
|
||||||
if (vendorId == null || vendorId.isEmpty) return <Shift>[];
|
if (vendorId == null || vendorId.isEmpty) return <Shift>[];
|
||||||
|
|
||||||
final QueryResult<dc.ListShiftRolesByVendorIdData, dc.ListShiftRolesByVendorIdVariables> response = await _service.connector
|
final QueryResult<
|
||||||
|
dc.ListShiftRolesByVendorIdData,
|
||||||
|
dc.ListShiftRolesByVendorIdVariables
|
||||||
|
>
|
||||||
|
response = await _service.connector
|
||||||
.listShiftRolesByVendorId(vendorId: vendorId)
|
.listShiftRolesByVendorId(vendorId: vendorId)
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
final List<dc.ListShiftRolesByVendorIdShiftRoles> allShiftRoles = response.data.shiftRoles;
|
final List<dc.ListShiftRolesByVendorIdShiftRoles> allShiftRoles =
|
||||||
|
response.data.shiftRoles;
|
||||||
|
|
||||||
// Fetch current applications to filter out already booked shifts
|
// Fetch current applications to filter out already booked shifts
|
||||||
final QueryResult<dc.GetApplicationsByStaffIdData, dc.GetApplicationsByStaffIdVariables> myAppsResponse = await _service.connector
|
final QueryResult<
|
||||||
|
dc.GetApplicationsByStaffIdData,
|
||||||
|
dc.GetApplicationsByStaffIdVariables
|
||||||
|
>
|
||||||
|
myAppsResponse = await _service.connector
|
||||||
.getApplicationsByStaffId(staffId: staffId)
|
.getApplicationsByStaffId(staffId: staffId)
|
||||||
.execute();
|
.execute();
|
||||||
final Set<String> appliedShiftIds =
|
final Set<String> appliedShiftIds = myAppsResponse.data.applications
|
||||||
myAppsResponse.data.applications.map((dc.GetApplicationsByStaffIdApplications a) => a.shiftId).toSet();
|
.map((dc.GetApplicationsByStaffIdApplications a) => a.shiftId)
|
||||||
|
.toSet();
|
||||||
|
|
||||||
final List<Shift> mappedShifts = <Shift>[];
|
final List<Shift> mappedShifts = <Shift>[];
|
||||||
for (final dc.ListShiftRolesByVendorIdShiftRoles sr in allShiftRoles) {
|
for (final dc.ListShiftRolesByVendorIdShiftRoles sr in allShiftRoles) {
|
||||||
@@ -67,6 +81,12 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
final DateTime? endDt = _service.toDateTime(sr.endTime);
|
final DateTime? endDt = _service.toDateTime(sr.endTime);
|
||||||
final DateTime? createdDt = _service.toDateTime(sr.createdAt);
|
final DateTime? createdDt = _service.toDateTime(sr.createdAt);
|
||||||
|
|
||||||
|
// Normalise orderType to uppercase for consistent checks in the UI.
|
||||||
|
// RECURRING → groups shifts into Multi-Day cards.
|
||||||
|
// PERMANENT → groups shifts into Long Term cards.
|
||||||
|
final String orderTypeStr = sr.shift.order.orderType.stringValue
|
||||||
|
.toUpperCase();
|
||||||
|
|
||||||
mappedShifts.add(
|
mappedShifts.add(
|
||||||
Shift(
|
Shift(
|
||||||
id: sr.shiftId,
|
id: sr.shiftId,
|
||||||
@@ -78,7 +98,9 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
location: sr.shift.location ?? '',
|
location: sr.shift.location ?? '',
|
||||||
locationAddress: sr.shift.locationAddress ?? '',
|
locationAddress: sr.shift.locationAddress ?? '',
|
||||||
date: shiftDate?.toIso8601String() ?? '',
|
date: shiftDate?.toIso8601String() ?? '',
|
||||||
startTime: startDt != null ? DateFormat('HH:mm').format(startDt) : '',
|
startTime: startDt != null
|
||||||
|
? DateFormat('HH:mm').format(startDt)
|
||||||
|
: '',
|
||||||
endTime: endDt != null ? DateFormat('HH:mm').format(endDt) : '',
|
endTime: endDt != null ? DateFormat('HH:mm').format(endDt) : '',
|
||||||
createdDate: createdDt?.toIso8601String() ?? '',
|
createdDate: createdDt?.toIso8601String() ?? '',
|
||||||
status: sr.shift.status?.stringValue.toLowerCase() ?? 'open',
|
status: sr.shift.status?.stringValue.toLowerCase() ?? 'open',
|
||||||
@@ -88,6 +110,10 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
filledSlots: sr.assigned ?? 0,
|
filledSlots: sr.assigned ?? 0,
|
||||||
latitude: sr.shift.latitude,
|
latitude: sr.shift.latitude,
|
||||||
longitude: sr.shift.longitude,
|
longitude: sr.shift.longitude,
|
||||||
|
// orderId + orderType power the grouping and type-badge logic in
|
||||||
|
// FindShiftsTab._groupMultiDayShifts and MyShiftCard._getShiftType.
|
||||||
|
orderId: sr.shift.orderId,
|
||||||
|
orderType: orderTypeStr,
|
||||||
breakInfo: BreakAdapter.fromData(
|
breakInfo: BreakAdapter.fromData(
|
||||||
isPaid: sr.isBreakPaid ?? false,
|
isPaid: sr.isBreakPaid ?? false,
|
||||||
breakTime: sr.breakType?.stringValue,
|
breakTime: sr.breakType?.stringValue,
|
||||||
@@ -125,7 +151,8 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
}) async {
|
}) async {
|
||||||
return _service.run(() async {
|
return _service.run(() async {
|
||||||
if (roleId != null && roleId.isNotEmpty) {
|
if (roleId != null && roleId.isNotEmpty) {
|
||||||
final QueryResult<dc.GetShiftRoleByIdData, dc.GetShiftRoleByIdVariables> roleResult = await _service.connector
|
final QueryResult<dc.GetShiftRoleByIdData, dc.GetShiftRoleByIdVariables>
|
||||||
|
roleResult = await _service.connector
|
||||||
.getShiftRoleById(shiftId: shiftId, roleId: roleId)
|
.getShiftRoleById(shiftId: shiftId, roleId: roleId)
|
||||||
.execute();
|
.execute();
|
||||||
final dc.GetShiftRoleByIdShiftRole? sr = roleResult.data.shiftRole;
|
final dc.GetShiftRoleByIdShiftRole? sr = roleResult.data.shiftRole;
|
||||||
@@ -137,13 +164,22 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
|
|
||||||
bool hasApplied = false;
|
bool hasApplied = false;
|
||||||
String status = 'open';
|
String status = 'open';
|
||||||
|
|
||||||
final QueryResult<dc.GetApplicationsByStaffIdData, dc.GetApplicationsByStaffIdVariables> appsResponse = await _service.connector
|
final QueryResult<
|
||||||
|
dc.GetApplicationsByStaffIdData,
|
||||||
|
dc.GetApplicationsByStaffIdVariables
|
||||||
|
>
|
||||||
|
appsResponse = await _service.connector
|
||||||
.getApplicationsByStaffId(staffId: staffId)
|
.getApplicationsByStaffId(staffId: staffId)
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
final dc.GetApplicationsByStaffIdApplications? app = appsResponse.data.applications
|
final dc.GetApplicationsByStaffIdApplications? app = appsResponse
|
||||||
.where((dc.GetApplicationsByStaffIdApplications a) => a.shiftId == shiftId && a.shiftRole.roleId == roleId)
|
.data
|
||||||
|
.applications
|
||||||
|
.where(
|
||||||
|
(dc.GetApplicationsByStaffIdApplications a) =>
|
||||||
|
a.shiftId == shiftId && a.shiftRole.roleId == roleId,
|
||||||
|
)
|
||||||
.firstOrNull;
|
.firstOrNull;
|
||||||
|
|
||||||
if (app != null) {
|
if (app != null) {
|
||||||
@@ -181,7 +217,8 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final QueryResult<dc.GetShiftByIdData, dc.GetShiftByIdVariables> result = await _service.connector.getShiftById(id: shiftId).execute();
|
final QueryResult<dc.GetShiftByIdData, dc.GetShiftByIdVariables> result =
|
||||||
|
await _service.connector.getShiftById(id: shiftId).execute();
|
||||||
final dc.GetShiftByIdShift? s = result.data.shift;
|
final dc.GetShiftByIdShift? s = result.data.shift;
|
||||||
if (s == null) return null;
|
if (s == null) return null;
|
||||||
|
|
||||||
@@ -190,17 +227,23 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
Break? breakInfo;
|
Break? breakInfo;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final QueryResult<dc.ListShiftRolesByShiftIdData, dc.ListShiftRolesByShiftIdVariables> rolesRes = await _service.connector
|
final QueryResult<
|
||||||
|
dc.ListShiftRolesByShiftIdData,
|
||||||
|
dc.ListShiftRolesByShiftIdVariables
|
||||||
|
>
|
||||||
|
rolesRes = await _service.connector
|
||||||
.listShiftRolesByShiftId(shiftId: shiftId)
|
.listShiftRolesByShiftId(shiftId: shiftId)
|
||||||
.execute();
|
.execute();
|
||||||
if (rolesRes.data.shiftRoles.isNotEmpty) {
|
if (rolesRes.data.shiftRoles.isNotEmpty) {
|
||||||
required = 0;
|
required = 0;
|
||||||
filled = 0;
|
filled = 0;
|
||||||
for (dc.ListShiftRolesByShiftIdShiftRoles r in rolesRes.data.shiftRoles) {
|
for (dc.ListShiftRolesByShiftIdShiftRoles r
|
||||||
|
in rolesRes.data.shiftRoles) {
|
||||||
required = (required ?? 0) + r.count;
|
required = (required ?? 0) + r.count;
|
||||||
filled = (filled ?? 0) + (r.assigned ?? 0);
|
filled = (filled ?? 0) + (r.assigned ?? 0);
|
||||||
}
|
}
|
||||||
final dc.ListShiftRolesByShiftIdShiftRoles firstRole = rolesRes.data.shiftRoles.first;
|
final dc.ListShiftRolesByShiftIdShiftRoles firstRole =
|
||||||
|
rolesRes.data.shiftRoles.first;
|
||||||
breakInfo = BreakAdapter.fromData(
|
breakInfo = BreakAdapter.fromData(
|
||||||
isPaid: firstRole.isBreakPaid ?? false,
|
isPaid: firstRole.isBreakPaid ?? false,
|
||||||
breakTime: firstRole.breakType?.stringValue,
|
breakTime: firstRole.breakType?.stringValue,
|
||||||
@@ -247,35 +290,53 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
final String targetRoleId = roleId ?? '';
|
final String targetRoleId = roleId ?? '';
|
||||||
if (targetRoleId.isEmpty) throw Exception('Missing role id.');
|
if (targetRoleId.isEmpty) throw Exception('Missing role id.');
|
||||||
|
|
||||||
final QueryResult<dc.GetShiftRoleByIdData, dc.GetShiftRoleByIdVariables> roleResult = await _service.connector
|
final QueryResult<dc.GetShiftRoleByIdData, dc.GetShiftRoleByIdVariables>
|
||||||
|
roleResult = await _service.connector
|
||||||
.getShiftRoleById(shiftId: shiftId, roleId: targetRoleId)
|
.getShiftRoleById(shiftId: shiftId, roleId: targetRoleId)
|
||||||
.execute();
|
.execute();
|
||||||
final dc.GetShiftRoleByIdShiftRole? role = roleResult.data.shiftRole;
|
final dc.GetShiftRoleByIdShiftRole? role = roleResult.data.shiftRole;
|
||||||
if (role == null) throw Exception('Shift role not found');
|
if (role == null) throw Exception('Shift role not found');
|
||||||
|
|
||||||
final QueryResult<dc.GetShiftByIdData, dc.GetShiftByIdVariables> shiftResult = await _service.connector.getShiftById(id: shiftId).execute();
|
final QueryResult<dc.GetShiftByIdData, dc.GetShiftByIdVariables>
|
||||||
|
shiftResult = await _service.connector
|
||||||
|
.getShiftById(id: shiftId)
|
||||||
|
.execute();
|
||||||
final dc.GetShiftByIdShift? shift = shiftResult.data.shift;
|
final dc.GetShiftByIdShift? shift = shiftResult.data.shift;
|
||||||
if (shift == null) throw Exception('Shift not found');
|
if (shift == null) throw Exception('Shift not found');
|
||||||
|
|
||||||
// Validate daily limit
|
// Validate daily limit
|
||||||
final DateTime? shiftDate = _service.toDateTime(shift.date);
|
final DateTime? shiftDate = _service.toDateTime(shift.date);
|
||||||
if (shiftDate != null) {
|
if (shiftDate != null) {
|
||||||
final DateTime dayStartUtc = DateTime.utc(shiftDate.year, shiftDate.month, shiftDate.day);
|
final DateTime dayStartUtc = DateTime.utc(
|
||||||
final DateTime dayEndUtc = dayStartUtc.add(const Duration(days: 1)).subtract(const Duration(microseconds: 1));
|
shiftDate.year,
|
||||||
|
shiftDate.month,
|
||||||
|
shiftDate.day,
|
||||||
|
);
|
||||||
|
final DateTime dayEndUtc = dayStartUtc
|
||||||
|
.add(const Duration(days: 1))
|
||||||
|
.subtract(const Duration(microseconds: 1));
|
||||||
|
|
||||||
final QueryResult<dc.VaidateDayStaffApplicationData, dc.VaidateDayStaffApplicationVariables> validationResponse = await _service.connector
|
final QueryResult<
|
||||||
|
dc.VaidateDayStaffApplicationData,
|
||||||
|
dc.VaidateDayStaffApplicationVariables
|
||||||
|
>
|
||||||
|
validationResponse = await _service.connector
|
||||||
.vaidateDayStaffApplication(staffId: staffId)
|
.vaidateDayStaffApplication(staffId: staffId)
|
||||||
.dayStart(_service.toTimestamp(dayStartUtc))
|
.dayStart(_service.toTimestamp(dayStartUtc))
|
||||||
.dayEnd(_service.toTimestamp(dayEndUtc))
|
.dayEnd(_service.toTimestamp(dayEndUtc))
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
if (validationResponse.data.applications.isNotEmpty) {
|
if (validationResponse.data.applications.isNotEmpty) {
|
||||||
throw Exception('The user already has a shift that day.');
|
throw Exception('The user already has a shift that day.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for existing application
|
// Check for existing application
|
||||||
final QueryResult<dc.GetApplicationByStaffShiftAndRoleData, dc.GetApplicationByStaffShiftAndRoleVariables> existingAppRes = await _service.connector
|
final QueryResult<
|
||||||
|
dc.GetApplicationByStaffShiftAndRoleData,
|
||||||
|
dc.GetApplicationByStaffShiftAndRoleVariables
|
||||||
|
>
|
||||||
|
existingAppRes = await _service.connector
|
||||||
.getApplicationByStaffShiftAndRole(
|
.getApplicationByStaffShiftAndRole(
|
||||||
staffId: staffId,
|
staffId: staffId,
|
||||||
shiftId: shiftId,
|
shiftId: shiftId,
|
||||||
@@ -295,14 +356,20 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
|
|
||||||
String? createdAppId;
|
String? createdAppId;
|
||||||
try {
|
try {
|
||||||
final OperationResult<dc.CreateApplicationData, dc.CreateApplicationVariables> createRes = await _service.connector.createApplication(
|
final OperationResult<
|
||||||
shiftId: shiftId,
|
dc.CreateApplicationData,
|
||||||
staffId: staffId,
|
dc.CreateApplicationVariables
|
||||||
roleId: targetRoleId,
|
>
|
||||||
status: dc.ApplicationStatus.CONFIRMED, // Matches existing logic
|
createRes = await _service.connector
|
||||||
origin: dc.ApplicationOrigin.STAFF,
|
.createApplication(
|
||||||
).execute();
|
shiftId: shiftId,
|
||||||
|
staffId: staffId,
|
||||||
|
roleId: targetRoleId,
|
||||||
|
status: dc.ApplicationStatus.CONFIRMED, // Matches existing logic
|
||||||
|
origin: dc.ApplicationOrigin.STAFF,
|
||||||
|
)
|
||||||
|
.execute();
|
||||||
|
|
||||||
createdAppId = createRes.data.application_insert.id;
|
createdAppId = createRes.data.application_insert.id;
|
||||||
|
|
||||||
await _service.connector
|
await _service.connector
|
||||||
@@ -317,7 +384,9 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Simple rollback attempt (not guaranteed)
|
// Simple rollback attempt (not guaranteed)
|
||||||
if (createdAppId != null) {
|
if (createdAppId != null) {
|
||||||
await _service.connector.deleteApplication(id: createdAppId).execute();
|
await _service.connector
|
||||||
|
.deleteApplication(id: createdAppId)
|
||||||
|
.execute();
|
||||||
}
|
}
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
@@ -325,11 +394,12 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> acceptShift({
|
Future<void> acceptShift({required String shiftId, required String staffId}) {
|
||||||
required String shiftId,
|
return _updateApplicationStatus(
|
||||||
required String staffId,
|
shiftId,
|
||||||
}) {
|
staffId,
|
||||||
return _updateApplicationStatus(shiftId, staffId, dc.ApplicationStatus.CONFIRMED);
|
dc.ApplicationStatus.CONFIRMED,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -337,7 +407,11 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
required String shiftId,
|
required String shiftId,
|
||||||
required String staffId,
|
required String staffId,
|
||||||
}) {
|
}) {
|
||||||
return _updateApplicationStatus(shiftId, staffId, dc.ApplicationStatus.REJECTED);
|
return _updateApplicationStatus(
|
||||||
|
shiftId,
|
||||||
|
staffId,
|
||||||
|
dc.ApplicationStatus.REJECTED,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -351,18 +425,24 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
@override
|
@override
|
||||||
Future<List<Shift>> getHistoryShifts({required String staffId}) async {
|
Future<List<Shift>> getHistoryShifts({required String staffId}) async {
|
||||||
return _service.run(() async {
|
return _service.run(() async {
|
||||||
final QueryResult<dc.ListCompletedApplicationsByStaffIdData, dc.ListCompletedApplicationsByStaffIdVariables> response = await _service.connector
|
final QueryResult<
|
||||||
|
dc.ListCompletedApplicationsByStaffIdData,
|
||||||
|
dc.ListCompletedApplicationsByStaffIdVariables
|
||||||
|
>
|
||||||
|
response = await _service.connector
|
||||||
.listCompletedApplicationsByStaffId(staffId: staffId)
|
.listCompletedApplicationsByStaffId(staffId: staffId)
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
final List<Shift> shifts = <Shift>[];
|
final List<Shift> shifts = <Shift>[];
|
||||||
for (final dc.ListCompletedApplicationsByStaffIdApplications app in response.data.applications) {
|
for (final dc.ListCompletedApplicationsByStaffIdApplications app
|
||||||
|
in response.data.applications) {
|
||||||
final String roleName = app.shiftRole.role.name;
|
final String roleName = app.shiftRole.role.name;
|
||||||
final String orderName = (app.shift.order.eventName ?? '').trim().isNotEmpty
|
final String orderName =
|
||||||
|
(app.shift.order.eventName ?? '').trim().isNotEmpty
|
||||||
? app.shift.order.eventName!
|
? app.shift.order.eventName!
|
||||||
: app.shift.order.business.businessName;
|
: app.shift.order.business.businessName;
|
||||||
final String title = '$roleName - $orderName';
|
final String title = '$roleName - $orderName';
|
||||||
|
|
||||||
final DateTime? shiftDate = _service.toDateTime(app.shift.date);
|
final DateTime? shiftDate = _service.toDateTime(app.shift.date);
|
||||||
final DateTime? startDt = _service.toDateTime(app.shiftRole.startTime);
|
final DateTime? startDt = _service.toDateTime(app.shiftRole.startTime);
|
||||||
final DateTime? endDt = _service.toDateTime(app.shiftRole.endTime);
|
final DateTime? endDt = _service.toDateTime(app.shiftRole.endTime);
|
||||||
@@ -379,7 +459,9 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
location: app.shift.location ?? '',
|
location: app.shift.location ?? '',
|
||||||
locationAddress: app.shift.order.teamHub.hubName,
|
locationAddress: app.shift.order.teamHub.hubName,
|
||||||
date: shiftDate?.toIso8601String() ?? '',
|
date: shiftDate?.toIso8601String() ?? '',
|
||||||
startTime: startDt != null ? DateFormat('HH:mm').format(startDt) : '',
|
startTime: startDt != null
|
||||||
|
? DateFormat('HH:mm').format(startDt)
|
||||||
|
: '',
|
||||||
endTime: endDt != null ? DateFormat('HH:mm').format(endDt) : '',
|
endTime: endDt != null ? DateFormat('HH:mm').format(endDt) : '',
|
||||||
createdDate: createdDt?.toIso8601String() ?? '',
|
createdDate: createdDt?.toIso8601String() ?? '',
|
||||||
status: 'completed', // Hardcoded as checked out implies completion
|
status: 'completed', // Hardcoded as checked out implies completion
|
||||||
@@ -406,7 +488,8 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
List<Shift> _mapApplicationsToShifts(List<dynamic> apps) {
|
List<Shift> _mapApplicationsToShifts(List<dynamic> apps) {
|
||||||
return apps.map((app) {
|
return apps.map((app) {
|
||||||
final String roleName = app.shiftRole.role.name;
|
final String roleName = app.shiftRole.role.name;
|
||||||
final String orderName = (app.shift.order.eventName ?? '').trim().isNotEmpty
|
final String orderName =
|
||||||
|
(app.shift.order.eventName ?? '').trim().isNotEmpty
|
||||||
? app.shift.order.eventName!
|
? app.shift.order.eventName!
|
||||||
: app.shift.order.business.businessName;
|
: app.shift.order.business.businessName;
|
||||||
final String title = '$roleName - $orderName';
|
final String title = '$roleName - $orderName';
|
||||||
@@ -418,7 +501,7 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
|
|
||||||
final bool hasCheckIn = app.checkInTime != null;
|
final bool hasCheckIn = app.checkInTime != null;
|
||||||
final bool hasCheckOut = app.checkOutTime != null;
|
final bool hasCheckOut = app.checkOutTime != null;
|
||||||
|
|
||||||
String status;
|
String status;
|
||||||
if (hasCheckOut) {
|
if (hasCheckOut) {
|
||||||
status = 'completed';
|
status = 'completed';
|
||||||
@@ -479,12 +562,20 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
) async {
|
) async {
|
||||||
return _service.run(() async {
|
return _service.run(() async {
|
||||||
// First try to find the application
|
// First try to find the application
|
||||||
final QueryResult<dc.GetApplicationsByStaffIdData, dc.GetApplicationsByStaffIdVariables> appsResponse = await _service.connector
|
final QueryResult<
|
||||||
|
dc.GetApplicationsByStaffIdData,
|
||||||
|
dc.GetApplicationsByStaffIdVariables
|
||||||
|
>
|
||||||
|
appsResponse = await _service.connector
|
||||||
.getApplicationsByStaffId(staffId: staffId)
|
.getApplicationsByStaffId(staffId: staffId)
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
final dc.GetApplicationsByStaffIdApplications? app = appsResponse.data.applications
|
final dc.GetApplicationsByStaffIdApplications? app = appsResponse
|
||||||
.where((dc.GetApplicationsByStaffIdApplications a) => a.shiftId == shiftId)
|
.data
|
||||||
|
.applications
|
||||||
|
.where(
|
||||||
|
(dc.GetApplicationsByStaffIdApplications a) => a.shiftId == shiftId,
|
||||||
|
)
|
||||||
.firstOrNull;
|
.firstOrNull;
|
||||||
|
|
||||||
if (app != null) {
|
if (app != null) {
|
||||||
@@ -494,19 +585,26 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
.execute();
|
.execute();
|
||||||
} else if (newStatus == dc.ApplicationStatus.REJECTED) {
|
} else if (newStatus == dc.ApplicationStatus.REJECTED) {
|
||||||
// If declining but no app found, create a rejected application
|
// If declining but no app found, create a rejected application
|
||||||
final QueryResult<dc.ListShiftRolesByShiftIdData, dc.ListShiftRolesByShiftIdVariables> rolesRes = await _service.connector
|
final QueryResult<
|
||||||
|
dc.ListShiftRolesByShiftIdData,
|
||||||
|
dc.ListShiftRolesByShiftIdVariables
|
||||||
|
>
|
||||||
|
rolesRes = await _service.connector
|
||||||
.listShiftRolesByShiftId(shiftId: shiftId)
|
.listShiftRolesByShiftId(shiftId: shiftId)
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
if (rolesRes.data.shiftRoles.isNotEmpty) {
|
if (rolesRes.data.shiftRoles.isNotEmpty) {
|
||||||
final dc.ListShiftRolesByShiftIdShiftRoles firstRole = rolesRes.data.shiftRoles.first;
|
final dc.ListShiftRolesByShiftIdShiftRoles firstRole =
|
||||||
await _service.connector.createApplication(
|
rolesRes.data.shiftRoles.first;
|
||||||
shiftId: shiftId,
|
await _service.connector
|
||||||
staffId: staffId,
|
.createApplication(
|
||||||
roleId: firstRole.id,
|
shiftId: shiftId,
|
||||||
status: dc.ApplicationStatus.REJECTED,
|
staffId: staffId,
|
||||||
origin: dc.ApplicationOrigin.STAFF,
|
roleId: firstRole.id,
|
||||||
).execute();
|
status: dc.ApplicationStatus.REJECTED,
|
||||||
|
origin: dc.ApplicationOrigin.STAFF,
|
||||||
|
)
|
||||||
|
.execute();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw Exception("Application not found for shift $shiftId");
|
throw Exception("Application not found for shift $shiftId");
|
||||||
@@ -514,4 +612,3 @@ class ShiftsConnectorRepositoryImpl implements ShiftsConnectorRepository {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
|
|
||||||
|
|
||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:core_localization/core_localization.dart';
|
import 'package:core_localization/core_localization.dart';
|
||||||
import 'package:krow_core/core.dart';
|
import 'package:krow_core/core.dart';
|
||||||
|
|
||||||
|
|
||||||
/// Card widget for displaying pending payment information, using design system tokens.
|
/// Card widget for displaying pending payment information, using design system tokens.
|
||||||
class PendingPaymentCard extends StatelessWidget {
|
class PendingPaymentCard extends StatelessWidget {
|
||||||
/// Creates a [PendingPaymentCard].
|
/// Creates a [PendingPaymentCard].
|
||||||
@@ -21,7 +19,10 @@ class PendingPaymentCard extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.all(UiConstants.space4),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
colors: [UiColors.primary.withOpacity(0.08), UiColors.primary.withOpacity(0.04)],
|
colors: [
|
||||||
|
UiColors.primary.withOpacity(0.08),
|
||||||
|
UiColors.primary.withOpacity(0.04),
|
||||||
|
],
|
||||||
begin: Alignment.centerLeft,
|
begin: Alignment.centerLeft,
|
||||||
end: Alignment.centerRight,
|
end: Alignment.centerRight,
|
||||||
),
|
),
|
||||||
@@ -59,7 +60,9 @@ class PendingPaymentCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
pendingI18n.subtitle,
|
pendingI18n.subtitle,
|
||||||
style: UiTypography.body3r.copyWith(color: UiColors.mutedForeground),
|
style: UiTypography.body3r.copyWith(
|
||||||
|
color: UiColors.mutedForeground,
|
||||||
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -70,10 +73,7 @@ class PendingPaymentCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text('\$285.00', style: UiTypography.headline4m),
|
||||||
'\$285.00',
|
|
||||||
style: UiTypography.headline4m,
|
|
||||||
),
|
|
||||||
SizedBox(width: UiConstants.space2),
|
SizedBox(width: UiConstants.space2),
|
||||||
Icon(
|
Icon(
|
||||||
UiIcons.chevronRight,
|
UiIcons.chevronRight,
|
||||||
|
|||||||
@@ -185,12 +185,15 @@ class _MyShiftCardState extends State<MyShiftCard> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
// Status Badge
|
// Badge row: shows the status label and the shift-type chip.
|
||||||
if (statusText.isNotEmpty)
|
// The type chip (One Day / Multi-Day / Long Term) is always
|
||||||
Padding(
|
// rendered when orderType is present — even for "open" find-shifts
|
||||||
padding: const EdgeInsets.only(bottom: UiConstants.space2),
|
// cards that may have no meaningful status text.
|
||||||
child: Row(
|
Padding(
|
||||||
children: [
|
padding: const EdgeInsets.only(bottom: UiConstants.space2),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
if (statusText.isNotEmpty) ...[
|
||||||
if (statusIcon != null)
|
if (statusIcon != null)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
@@ -221,30 +224,31 @@ class _MyShiftCardState extends State<MyShiftCard> {
|
|||||||
letterSpacing: 0.5,
|
letterSpacing: 0.5,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// Shift Type Badge (Order type)
|
const SizedBox(width: UiConstants.space2),
|
||||||
if ((widget.shift.orderType ?? '').isNotEmpty) ...[
|
|
||||||
const SizedBox(width: UiConstants.space2),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: UiConstants.space2,
|
|
||||||
vertical: 2,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: UiColors.background,
|
|
||||||
borderRadius: UiConstants.radiusSm,
|
|
||||||
border: Border.all(color: UiColors.border),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
_getShiftType(),
|
|
||||||
style: UiTypography.footnote2m.copyWith(
|
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
),
|
// Type badge — driven by RECURRING / PERMANENT / one-day
|
||||||
|
// order data and always visible so users can filter
|
||||||
|
// Find Shifts cards at a glance.
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space2,
|
||||||
|
vertical: 2,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: UiColors.background,
|
||||||
|
borderRadius: UiConstants.radiusSm,
|
||||||
|
border: Border.all(color: UiColors.border),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
_getShiftType(),
|
||||||
|
style: UiTypography.footnote2m.copyWith(
|
||||||
|
color: UiColors.textSecondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|||||||
@@ -85,11 +85,13 @@ class _FindShiftsTabState extends State<FindShiftsTab> {
|
|||||||
|
|
||||||
final Shift first = group.first;
|
final Shift first = group.first;
|
||||||
final List<ShiftSchedule> schedules = group
|
final List<ShiftSchedule> schedules = group
|
||||||
.map((s) => ShiftSchedule(
|
.map(
|
||||||
date: s.date,
|
(s) => ShiftSchedule(
|
||||||
startTime: s.startTime,
|
date: s.date,
|
||||||
endTime: s.endTime,
|
startTime: s.startTime,
|
||||||
))
|
endTime: s.endTime,
|
||||||
|
),
|
||||||
|
)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
result.add(
|
result.add(
|
||||||
|
|||||||
Reference in New Issue
Block a user