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'; import '../../entities/shifts/shift.dart';
/// Adapter for Shift related data. /// Adapter for Shift related data.
class ShiftAdapter { class ShiftAdapter {
/// Maps application data to a Shift entity.
// 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, /// This method handles the common mapping logic used across different
// we might put the logic in Repo or make this accept dynamic/Map if strictly required. /// repositories when converting application data from Data Connect to
// For now, placeholders or simple status helpers. /// 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: case dc.ApplicationStatus.PENDING:
return CoverageWorkerStatus.pending; return CoverageWorkerStatus.pending;
case dc.ApplicationStatus.ACCEPTED: case dc.ApplicationStatus.ACCEPTED:
return CoverageWorkerStatus.accepted; return CoverageWorkerStatus.confirmed;
case dc.ApplicationStatus.REJECTED: case dc.ApplicationStatus.REJECTED:
return CoverageWorkerStatus.rejected; return CoverageWorkerStatus.rejected;
case dc.ApplicationStatus.CONFIRMED: case dc.ApplicationStatus.CONFIRMED:

View File

@@ -32,28 +32,40 @@ class HomeRepositoryImpl implements HomeRepository {
Future<List<Shift>> _getShiftsForDate(DateTime date) async { Future<List<Shift>> _getShiftsForDate(DateTime date) async {
try { 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 final response = await ExampleConnector.instance
.getApplicationsByStaffId(staffId: _currentStaffId) .getApplicationsByStaffId(staffId: staffId)
.dayStart(_toTimestamp(start))
.dayEnd(_toTimestamp(end))
.execute(); .execute();
final targetYmd = DateFormat('yyyy-MM-dd').format(date); // Filter for ACCEPTED applications (same logic as shifts_repository_impl)
final apps = response.data.applications.where(
return response.data.applications (app) => (app.status is Known && (app.status as Known).value == ApplicationStatus.ACCEPTED) || (app.status is Known && (app.status as Known).value == ApplicationStatus.CONFIRMED)
.where((app) { );
final shiftDate = app.shift.date?.toDate();
if (shiftDate == null) return false; final List<Shift> shifts = [];
for (final app in apps) {
final isDateMatch = DateFormat('yyyy-MM-dd').format(shiftDate) == targetYmd; shifts.add(_mapApplicationToShift(app));
final isAssigned = app.status is Known && ((app.status as Known).value == ApplicationStatus.ACCEPTED || (app.status as Known).value == ApplicationStatus.CONFIRMED); }
return isDateMatch && isAssigned; return shifts;
})
.map((app) => _mapApplicationToShift(app))
.toList();
} catch (e) { } catch (e) {
return []; 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 @override
Future<List<Shift>> getRecommendedShifts() async { Future<List<Shift>> getRecommendedShifts() async {
@@ -93,21 +105,26 @@ class HomeRepositoryImpl implements HomeRepository {
final s = app.shift; final s = app.shift;
final r = app.shiftRole; final r = app.shiftRole;
return Shift( return ShiftAdapter.fromApplicationData(
id: s.id, shiftId: s.id,
title: r.role.name, roleId: r.roleId,
clientName: s.order.business.businessName, roleName: r.role.name,
hourlyRate: r.role.costPerHour, businessName: s.order.business.businessName,
location: s.location ?? 'Unknown', companyLogoUrl: s.order.business.companyLogoUrl,
locationAddress: s.location ?? '', costPerHour: r.role.costPerHour,
date: s.date?.toDate().toIso8601String() ?? '', shiftLocation: s.location,
startTime: DateFormat('HH:mm').format(r.startTime?.toDate() ?? DateTime.now()), teamHubName: s.order.teamHub.hubName,
endTime: DateFormat('HH:mm').format(r.endTime?.toDate() ?? DateTime.now()), shiftDate: s.date?.toDate(),
createdDate: app.createdAt?.toDate().toIso8601String() ?? '', startTime: r.startTime?.toDate(),
tipsAvailable: false, // Not in API endTime: r.endTime?.toDate(),
mealProvided: false, // Not in API createdAt: app.createdAt?.toDate(),
managers: [], // Not in this query status: 'confirmed',
description: null, 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; 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 @override
Future<List<Shift>> getMyShifts({ Future<List<Shift>> getMyShifts({
required DateTime start, required DateTime start,
required DateTime end, required DateTime end,
}) async { }) async {
return _fetchApplications( return _fetchApplications(
dc.ApplicationStatus.ACCEPTED, [dc.ApplicationStatus.ACCEPTED, dc.ApplicationStatus.CONFIRMED],
start: start, start: start,
end: end, end: end,
); );
@@ -91,12 +120,12 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
@override @override
Future<List<Shift>> getPendingAssignments() async { Future<List<Shift>> getPendingAssignments() async {
return _fetchApplications(dc.ApplicationStatus.PENDING); return _fetchApplications([dc.ApplicationStatus.PENDING]);
} }
@override @override
Future<List<Shift>> getCancelledShifts() async { Future<List<Shift>> getCancelledShifts() async {
return _fetchApplications(dc.ApplicationStatus.REJECTED); return _fetchApplications([dc.ApplicationStatus.REJECTED]);
} }
@override @override
@@ -112,37 +141,10 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
_shiftToAppIdMap[app.shift.id] = app.id; _shiftToAppIdMap[app.shift.id] = app.id;
_appToRoleIdMap[app.id] = app.shiftRole.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( shifts.add(
Shift( _mapApplicationToShift(
id: app.shift.id, app,
roleId: app.shiftRole.roleId, _mapStatus(dc.ApplicationStatus.CHECKED_OUT),
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,
), ),
); );
} }
@@ -153,7 +155,7 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
} }
Future<List<Shift>> _fetchApplications( Future<List<Shift>> _fetchApplications(
dc.ApplicationStatus status, { List<dc.ApplicationStatus> statuses, {
DateTime? start, DateTime? start,
DateTime? end, DateTime? end,
}) async { }) async {
@@ -167,8 +169,9 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
} }
final response = await query.execute(); final response = await query.execute();
final statusNames = statuses.map((s) => s.name).toSet();
final apps = response.data.applications.where( final apps = response.data.applications.where(
(app) => app.status.stringValue == status.name, (app) => statusNames.contains(app.status.stringValue),
); );
final List<Shift> shifts = []; final List<Shift> shifts = [];
@@ -176,40 +179,12 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
_shiftToAppIdMap[app.shift.id] = app.id; _shiftToAppIdMap[app.shift.id] = app.id;
_appToRoleIdMap[app.id] = app.shiftRole.id; _appToRoleIdMap[app.id] = app.shiftRole.id;
final String roleName = app.shiftRole.role.name; // Use the first matching status for mapping
final String orderName = final matchingStatus = statuses.firstWhere(
(app.shift.order.eventName ?? '').trim().isNotEmpty (s) => s.name == app.status.stringValue,
? app.shift.order.eventName! orElse: () => statuses.first,
: 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,
),
); );
shifts.add(_mapApplicationToShift(app, _mapStatus(matchingStatus)));
} }
return shifts; return shifts;
} catch (e) { } catch (e) {
@@ -491,7 +466,7 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
shiftId: shiftId, shiftId: shiftId,
staffId: staffId, staffId: staffId,
roleId: targetRoleId, roleId: targetRoleId,
status: dc.ApplicationStatus.ACCEPTED, status: dc.ApplicationStatus.CONFIRMED,
origin: dc.ApplicationOrigin.STAFF, origin: dc.ApplicationOrigin.STAFF,
) )
// TODO: this should be PENDING so a vendor can accept it. // TODO: this should be PENDING so a vendor can accept it.