feat: Integrate ShiftAdapter for mapping application data to Shift entities and update status handling in Coverage and Shifts repositories

This commit is contained in:
Achintha Isuru
2026-02-01 22:18:07 -05:00
parent 6d0d7dcbd2
commit 476d697dc3
4 changed files with 146 additions and 105 deletions

View File

@@ -1,10 +1,59 @@
import 'package:intl/intl.dart';
import '../../entities/shifts/shift.dart';
/// Adapter for Shift related data.
class ShiftAdapter {
// Note: Conversion logic will likely live in RepoImpl or here if we pass raw objects.
// Given we are dealing with generated types that aren't exported by domain,
// we might put the logic in Repo or make this accept dynamic/Map if strictly required.
// For now, placeholders or simple status helpers.
/// Maps application data to a Shift entity.
///
/// This method handles the common mapping logic used across different
/// repositories when converting application data from Data Connect to
/// domain Shift entities.
static Shift fromApplicationData({
required String shiftId,
required String roleId,
required String roleName,
required String businessName,
String? companyLogoUrl,
required double costPerHour,
String? shiftLocation,
required String teamHubName,
DateTime? shiftDate,
DateTime? startTime,
DateTime? endTime,
DateTime? createdAt,
required String status,
String? description,
int? durationDays,
required int count,
int? assigned,
String? eventName,
bool hasApplied = false,
}) {
final String orderName = (eventName ?? '').trim().isNotEmpty
? eventName!
: businessName;
final String title = '$roleName - $orderName';
return Shift(
id: shiftId,
roleId: roleId,
title: title,
clientName: businessName,
logoUrl: companyLogoUrl,
hourlyRate: costPerHour,
location: shiftLocation ?? '',
locationAddress: teamHubName,
date: shiftDate?.toIso8601String() ?? '',
startTime: startTime != null ? DateFormat('HH:mm').format(startTime) : '',
endTime: endTime != null ? DateFormat('HH:mm').format(endTime) : '',
createdDate: createdAt?.toIso8601String() ?? '',
status: status,
description: description,
durationDays: durationDays,
requiredSlots: count,
filledSlots: assigned ?? 0,
hasApplied: hasApplied,
);
}
}

View File

@@ -178,7 +178,7 @@ class CoverageRepositoryImpl implements CoverageRepository {
case dc.ApplicationStatus.PENDING:
return CoverageWorkerStatus.pending;
case dc.ApplicationStatus.ACCEPTED:
return CoverageWorkerStatus.accepted;
return CoverageWorkerStatus.confirmed;
case dc.ApplicationStatus.REJECTED:
return CoverageWorkerStatus.rejected;
case dc.ApplicationStatus.CONFIRMED:

View File

@@ -32,28 +32,40 @@ class HomeRepositoryImpl implements HomeRepository {
Future<List<Shift>> _getShiftsForDate(DateTime date) async {
try {
final staffId = _currentStaffId;
// Create start and end timestamps for the target date
final DateTime start = DateTime(date.year, date.month, date.day);
final DateTime end = DateTime(date.year, date.month, date.day, 23, 59, 59, 999);
final response = await ExampleConnector.instance
.getApplicationsByStaffId(staffId: _currentStaffId)
.getApplicationsByStaffId(staffId: staffId)
.dayStart(_toTimestamp(start))
.dayEnd(_toTimestamp(end))
.execute();
final targetYmd = DateFormat('yyyy-MM-dd').format(date);
return response.data.applications
.where((app) {
final shiftDate = app.shift.date?.toDate();
if (shiftDate == null) return false;
final isDateMatch = DateFormat('yyyy-MM-dd').format(shiftDate) == targetYmd;
final isAssigned = app.status is Known && ((app.status as Known).value == ApplicationStatus.ACCEPTED || (app.status as Known).value == ApplicationStatus.CONFIRMED);
return isDateMatch && isAssigned;
})
.map((app) => _mapApplicationToShift(app))
.toList();
// Filter for ACCEPTED applications (same logic as shifts_repository_impl)
final apps = response.data.applications.where(
(app) => (app.status is Known && (app.status as Known).value == ApplicationStatus.ACCEPTED) || (app.status is Known && (app.status as Known).value == ApplicationStatus.CONFIRMED)
);
final List<Shift> shifts = [];
for (final app in apps) {
shifts.add(_mapApplicationToShift(app));
}
return shifts;
} catch (e) {
return [];
}
}
Timestamp _toTimestamp(DateTime dateTime) {
final DateTime utc = dateTime.toUtc();
final int seconds = utc.millisecondsSinceEpoch ~/ 1000;
final int nanoseconds = (utc.microsecondsSinceEpoch % 1000000) * 1000;
return Timestamp(nanoseconds, seconds);
}
@override
Future<List<Shift>> getRecommendedShifts() async {
@@ -93,21 +105,26 @@ class HomeRepositoryImpl implements HomeRepository {
final s = app.shift;
final r = app.shiftRole;
return Shift(
id: s.id,
title: r.role.name,
clientName: s.order.business.businessName,
hourlyRate: r.role.costPerHour,
location: s.location ?? 'Unknown',
locationAddress: s.location ?? '',
date: s.date?.toDate().toIso8601String() ?? '',
startTime: DateFormat('HH:mm').format(r.startTime?.toDate() ?? DateTime.now()),
endTime: DateFormat('HH:mm').format(r.endTime?.toDate() ?? DateTime.now()),
createdDate: app.createdAt?.toDate().toIso8601String() ?? '',
tipsAvailable: false, // Not in API
mealProvided: false, // Not in API
managers: [], // Not in this query
description: null,
return ShiftAdapter.fromApplicationData(
shiftId: s.id,
roleId: r.roleId,
roleName: r.role.name,
businessName: s.order.business.businessName,
companyLogoUrl: s.order.business.companyLogoUrl,
costPerHour: r.role.costPerHour,
shiftLocation: s.location,
teamHubName: s.order.teamHub.hubName,
shiftDate: s.date?.toDate(),
startTime: r.startTime?.toDate(),
endTime: r.endTime?.toDate(),
createdAt: app.createdAt?.toDate(),
status: 'confirmed',
description: s.description,
durationDays: s.durationDays,
count: r.count,
assigned: r.assigned,
eventName: s.order.eventName,
hasApplied: true,
);
}

View File

@@ -77,13 +77,42 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
return null;
}
/// Helper method to map Data Connect application to domain Shift using ShiftAdapter.
Shift _mapApplicationToShift(
dynamic app,
String status, {
bool hasApplied = true,
}) {
return ShiftAdapter.fromApplicationData(
shiftId: app.shift.id,
roleId: app.shiftRole.roleId,
roleName: app.shiftRole.role.name,
businessName: app.shift.order.business.businessName,
companyLogoUrl: app.shift.order.business.companyLogoUrl,
costPerHour: app.shiftRole.role.costPerHour,
shiftLocation: app.shift.location,
teamHubName: app.shift.order.teamHub.hubName,
shiftDate: _toDateTime(app.shift.date),
startTime: _toDateTime(app.shiftRole.startTime),
endTime: _toDateTime(app.shiftRole.endTime),
createdAt: _toDateTime(app.createdAt),
status: status,
description: app.shift.description,
durationDays: app.shift.durationDays,
count: app.shiftRole.count,
assigned: app.shiftRole.assigned,
eventName: app.shift.order.eventName,
hasApplied: hasApplied,
);
}
@override
Future<List<Shift>> getMyShifts({
required DateTime start,
required DateTime end,
}) async {
return _fetchApplications(
dc.ApplicationStatus.ACCEPTED,
[dc.ApplicationStatus.ACCEPTED, dc.ApplicationStatus.CONFIRMED],
start: start,
end: end,
);
@@ -91,12 +120,12 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
@override
Future<List<Shift>> getPendingAssignments() async {
return _fetchApplications(dc.ApplicationStatus.PENDING);
return _fetchApplications([dc.ApplicationStatus.PENDING]);
}
@override
Future<List<Shift>> getCancelledShifts() async {
return _fetchApplications(dc.ApplicationStatus.REJECTED);
return _fetchApplications([dc.ApplicationStatus.REJECTED]);
}
@override
@@ -112,37 +141,10 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
_shiftToAppIdMap[app.shift.id] = app.id;
_appToRoleIdMap[app.id] = app.shiftRole.id;
final String roleName = app.shiftRole.role.name;
final String orderName =
(app.shift.order.eventName ?? '').trim().isNotEmpty
? app.shift.order.eventName!
: app.shift.order.business.businessName;
final String title = '$roleName - $orderName';
final DateTime? shiftDate = _toDateTime(app.shift.date);
final DateTime? startDt = _toDateTime(app.shiftRole.startTime);
final DateTime? endDt = _toDateTime(app.shiftRole.endTime);
final DateTime? createdDt = _toDateTime(app.createdAt);
shifts.add(
Shift(
id: app.shift.id,
roleId: app.shiftRole.roleId,
title: title,
clientName: app.shift.order.business.businessName,
logoUrl: app.shift.order.business.companyLogoUrl,
hourlyRate: app.shiftRole.role.costPerHour,
location: app.shift.location ?? '',
locationAddress: app.shift.order.teamHub.hubName,
date: shiftDate?.toIso8601String() ?? '',
startTime: startDt != null ? DateFormat('HH:mm').format(startDt) : '',
endTime: endDt != null ? DateFormat('HH:mm').format(endDt) : '',
createdDate: createdDt?.toIso8601String() ?? '',
status: _mapStatus(dc.ApplicationStatus.CHECKED_OUT),
description: app.shift.description,
durationDays: app.shift.durationDays,
requiredSlots: app.shiftRole.count,
filledSlots: app.shiftRole.assigned ?? 0,
hasApplied: true,
_mapApplicationToShift(
app,
_mapStatus(dc.ApplicationStatus.CHECKED_OUT),
),
);
}
@@ -153,7 +155,7 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
}
Future<List<Shift>> _fetchApplications(
dc.ApplicationStatus status, {
List<dc.ApplicationStatus> statuses, {
DateTime? start,
DateTime? end,
}) async {
@@ -167,8 +169,9 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
}
final response = await query.execute();
final statusNames = statuses.map((s) => s.name).toSet();
final apps = response.data.applications.where(
(app) => app.status.stringValue == status.name,
(app) => statusNames.contains(app.status.stringValue),
);
final List<Shift> shifts = [];
@@ -176,40 +179,12 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
_shiftToAppIdMap[app.shift.id] = app.id;
_appToRoleIdMap[app.id] = app.shiftRole.id;
final String roleName = app.shiftRole.role.name;
final String orderName =
(app.shift.order.eventName ?? '').trim().isNotEmpty
? app.shift.order.eventName!
: app.shift.order.business.businessName;
final String title = '$roleName - $orderName';
final DateTime? shiftDate = _toDateTime(app.shift.date);
final DateTime? startDt = _toDateTime(app.shiftRole.startTime);
final DateTime? endDt = _toDateTime(app.shiftRole.endTime);
final DateTime? createdDt = _toDateTime(app.createdAt);
// Override status to reflect the application state (e.g., CHECKED_OUT, ACCEPTED)
shifts.add(
Shift(
id: app.shift.id,
roleId: app.shiftRole.roleId,
title: title,
clientName: app.shift.order.business.businessName,
logoUrl: app.shift.order.business.companyLogoUrl,
hourlyRate: app.shiftRole.role.costPerHour,
location: app.shift.location ?? '',
locationAddress: app.shift.order.teamHub.hubName,
date: shiftDate?.toIso8601String() ?? '',
startTime: startDt != null ? DateFormat('HH:mm').format(startDt) : '',
endTime: endDt != null ? DateFormat('HH:mm').format(endDt) : '',
createdDate: createdDt?.toIso8601String() ?? '',
status: _mapStatus(status),
description: app.shift.description,
durationDays: app.shift.durationDays,
requiredSlots: app.shiftRole.count,
filledSlots: app.shiftRole.assigned ?? 0,
hasApplied: true,
),
// Use the first matching status for mapping
final matchingStatus = statuses.firstWhere(
(s) => s.name == app.status.stringValue,
orElse: () => statuses.first,
);
shifts.add(_mapApplicationToShift(app, _mapStatus(matchingStatus)));
}
return shifts;
} catch (e) {
@@ -491,7 +466,7 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
shiftId: shiftId,
staffId: staffId,
roleId: targetRoleId,
status: dc.ApplicationStatus.ACCEPTED,
status: dc.ApplicationStatus.CONFIRMED,
origin: dc.ApplicationOrigin.STAFF,
)
// TODO: this should be PENDING so a vendor can accept it.