From 476d697dc3000dcb164a8c2be819580ed0cab012 Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Sun, 1 Feb 2026 22:18:07 -0500 Subject: [PATCH] feat: Integrate ShiftAdapter for mapping application data to Shift entities and update status handling in Coverage and Shifts repositories --- .../src/adapters/shifts/shift_adapter.dart | 59 ++++++++- .../coverage_repository_impl.dart | 2 +- .../repositories/home_repository_impl.dart | 77 +++++++----- .../shifts_repository_impl.dart | 113 +++++++----------- 4 files changed, 146 insertions(+), 105 deletions(-) diff --git a/apps/mobile/packages/domain/lib/src/adapters/shifts/shift_adapter.dart b/apps/mobile/packages/domain/lib/src/adapters/shifts/shift_adapter.dart index 07cab44a..6022d327 100644 --- a/apps/mobile/packages/domain/lib/src/adapters/shifts/shift_adapter.dart +++ b/apps/mobile/packages/domain/lib/src/adapters/shifts/shift_adapter.dart @@ -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, + ); + } } + diff --git a/apps/mobile/packages/features/client/client_coverage/lib/src/data/repositories_impl/coverage_repository_impl.dart b/apps/mobile/packages/features/client/client_coverage/lib/src/data/repositories_impl/coverage_repository_impl.dart index 9ee38782..cfecec36 100644 --- a/apps/mobile/packages/features/client/client_coverage/lib/src/data/repositories_impl/coverage_repository_impl.dart +++ b/apps/mobile/packages/features/client/client_coverage/lib/src/data/repositories_impl/coverage_repository_impl.dart @@ -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: diff --git a/apps/mobile/packages/features/staff/home/lib/src/data/repositories/home_repository_impl.dart b/apps/mobile/packages/features/staff/home/lib/src/data/repositories/home_repository_impl.dart index 40e6ddfe..6769036f 100644 --- a/apps/mobile/packages/features/staff/home/lib/src/data/repositories/home_repository_impl.dart +++ b/apps/mobile/packages/features/staff/home/lib/src/data/repositories/home_repository_impl.dart @@ -32,28 +32,40 @@ class HomeRepositoryImpl implements HomeRepository { Future> _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 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> 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, ); } diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/data/repositories_impl/shifts_repository_impl.dart b/apps/mobile/packages/features/staff/shifts/lib/src/data/repositories_impl/shifts_repository_impl.dart index 347f389f..8376b88b 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/data/repositories_impl/shifts_repository_impl.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/data/repositories_impl/shifts_repository_impl.dart @@ -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> 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> getPendingAssignments() async { - return _fetchApplications(dc.ApplicationStatus.PENDING); + return _fetchApplications([dc.ApplicationStatus.PENDING]); } @override Future> 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> _fetchApplications( - dc.ApplicationStatus status, { + List 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 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.