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 d28e079b..ca9d23d1 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 @@ -37,7 +37,9 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface { } try { - final response = await _dataConnect.getStaffByUserId(userId: user.uid).execute(); + final response = await _dataConnect + .getStaffByUserId(userId: user.uid) + .execute(); if (response.data.staffs.isNotEmpty) { _cachedStaffId = response.data.staffs.first.id; return _cachedStaffId!; @@ -47,9 +49,9 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface { } // 4. Fallback (should ideally not happen if DB is seeded) - return user.uid; + return user.uid; } - + DateTime? _toDateTime(dynamic t) { if (t == null) return null; DateTime? dt; @@ -79,7 +81,7 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface { Future> getMyShifts() async { return _fetchApplications(dc.ApplicationStatus.ACCEPTED); } - + @override Future> getPendingAssignments() async { return _fetchApplications(dc.ApplicationStatus.PENDING); @@ -102,35 +104,39 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface { .getApplicationsByStaffId(staffId: staffId) .execute(); - final apps = response.data.applications.where((app) => app.status.stringValue == status.name); + final apps = response.data.applications.where( + (app) => app.status.stringValue == status.name, + ); final List shifts = []; for (final app in apps) { - _shiftToAppIdMap[app.shift.id] = app.id; - _appToRoleIdMap[app.id] = app.shiftRole.id; - - final shift = await _getShiftDetails(app.shift.id); - if (shift != null) { - // Override status to reflect the application state (e.g., CHECKED_OUT, ACCEPTED) - shifts.add(Shift( - id: shift.id, - title: shift.title, - clientName: shift.clientName, - logoUrl: shift.logoUrl, - hourlyRate: shift.hourlyRate, - location: shift.location, - locationAddress: shift.locationAddress, - date: shift.date, - startTime: shift.startTime, - endTime: shift.endTime, - createdDate: shift.createdDate, - status: _mapStatus(status), - description: shift.description, - durationDays: shift.durationDays, - requiredSlots: shift.requiredSlots, - filledSlots: shift.filledSlots, - )); - } + _shiftToAppIdMap[app.shift.id] = app.id; + _appToRoleIdMap[app.id] = app.shiftRole.id; + + final shift = await _getShiftDetails(app.shift.id); + if (shift != null) { + // Override status to reflect the application state (e.g., CHECKED_OUT, ACCEPTED) + shifts.add( + Shift( + id: shift.id, + title: shift.title, + clientName: shift.clientName, + logoUrl: shift.logoUrl, + hourlyRate: shift.hourlyRate, + location: shift.location, + locationAddress: shift.locationAddress, + date: shift.date, + startTime: shift.startTime, + endTime: shift.endTime, + createdDate: shift.createdDate, + status: _mapStatus(status), + description: shift.description, + durationDays: shift.durationDays, + requiredSlots: shift.requiredSlots, + filledSlots: shift.filledSlots, + ), + ); + } } return shifts; } catch (e) { @@ -157,8 +163,7 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface { @override Future> getAvailableShifts(String query, String type) async { try { - final String? vendorId = - dc.StaffSessionStore.instance.session?.ownerId; + final String? vendorId = dc.StaffSessionStore.instance.session?.ownerId; if (vendorId == null || vendorId.isEmpty) { return []; } @@ -184,8 +189,9 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface { location: sr.shift.location ?? '', locationAddress: sr.shift.locationAddress ?? '', date: startDt?.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) : '', createdDate: createdDt?.toIso8601String() ?? '', status: sr.shift.status?.stringValue.toLowerCase() ?? 'open', @@ -217,7 +223,7 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface { Future getShiftDetails(String shiftId, {String? roleId}) async { return _getShiftDetails(shiftId, roleId: roleId); } - + Future _getShiftDetails(String shiftId, {String? roleId}) async { try { if (roleId != null && roleId.isNotEmpty) { @@ -235,12 +241,12 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface { bool hasApplied = false; String status = 'open'; if (staffId != null) { - final apps = - await _dataConnect.getApplicationsByStaffId(staffId: staffId).execute(); + final apps = await _dataConnect + .getApplicationsByStaffId(staffId: staffId) + .execute(); final app = apps.data.applications .where( - (a) => - a.shiftId == shiftId && a.shiftRole.roleId == roleId, + (a) => a.shiftId == shiftId && a.shiftRole.roleId == roleId, ) .firstOrNull; if (app != null) { @@ -260,8 +266,8 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface { clientName: sr.shift.order.business.businessName, logoUrl: sr.shift.order.business.companyLogoUrl, hourlyRate: sr.role.costPerHour, - location: sr.shift.order.teamHub.hubName, - locationAddress: '', + location: sr.shift.location ?? sr.shift.order.teamHub.hubName, + locationAddress: sr.shift.locationAddress ?? '', date: startDt?.toIso8601String() ?? '', startTime: startDt != null ? DateFormat('HH:mm').format(startDt) : '', endTime: endDt != null ? DateFormat('HH:mm').format(endDt) : '', @@ -279,19 +285,21 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface { final result = await _dataConnect.getShiftById(id: shiftId).execute(); final s = result.data.shift; if (s == null) return null; - + int? required; int? filled; try { - final rolesRes = await _dataConnect.listShiftRolesByShiftId(shiftId: shiftId).execute(); - if (rolesRes.data.shiftRoles.isNotEmpty) { - required = 0; - filled = 0; - for(var r in rolesRes.data.shiftRoles) { - required = (required ?? 0) + r.count; - filled = (filled ?? 0) + (r.assigned ?? 0); - } + final rolesRes = await _dataConnect + .listShiftRolesByShiftId(shiftId: shiftId) + .execute(); + if (rolesRes.data.shiftRoles.isNotEmpty) { + required = 0; + filled = 0; + for (var r in rolesRes.data.shiftRoles) { + required = (required ?? 0) + r.count; + filled = (filled ?? 0) + (r.assigned ?? 0); } + } } catch (_) {} final startDt = _toDateTime(s.startTime); @@ -299,22 +307,22 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface { final createdDt = _toDateTime(s.createdAt); return Shift( - id: s.id, - title: s.title, - clientName: s.order.business.businessName, - logoUrl: null, - hourlyRate: s.cost ?? 0.0, - location: s.location ?? '', - locationAddress: s.locationAddress ?? '', - date: startDt?.toIso8601String() ?? '', - startTime: startDt != null ? DateFormat('HH:mm').format(startDt) : '', - endTime: endDt != null ? DateFormat('HH:mm').format(endDt) : '', - createdDate: createdDt?.toIso8601String() ?? '', - status: s.status?.stringValue ?? 'OPEN', - description: s.description, - durationDays: s.durationDays, - requiredSlots: required, - filledSlots: filled, + id: s.id, + title: s.title, + clientName: s.order.business.businessName, + logoUrl: null, + hourlyRate: s.cost ?? 0.0, + location: s.location ?? '', + locationAddress: s.locationAddress ?? '', + date: startDt?.toIso8601String() ?? '', + startTime: startDt != null ? DateFormat('HH:mm').format(startDt) : '', + endTime: endDt != null ? DateFormat('HH:mm').format(endDt) : '', + createdDate: createdDt?.toIso8601String() ?? '', + status: s.status?.stringValue ?? 'OPEN', + description: s.description, + durationDays: s.durationDays, + requiredSlots: required, + filledSlots: filled, ); } catch (e) { return null; @@ -331,8 +339,9 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface { String targetRoleId = roleId ?? ''; if (targetRoleId.isEmpty) { - final rolesResult = - await _dataConnect.listShiftRolesByShiftId(shiftId: shiftId).execute(); + final rolesResult = await _dataConnect + .listShiftRolesByShiftId(shiftId: shiftId) + .execute(); if (rolesResult.data.shiftRoles.isEmpty) { throw Exception('No open roles for this shift'); } @@ -384,10 +393,7 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface { .execute(); updatedRole = true; - await _dataConnect - .updateShift(id: shiftId) - .filled(filled + 1) - .execute(); + await _dataConnect.updateShift(id: shiftId).filled(filled + 1).execute(); updatedShift = true; } catch (e) { if (updatedShift) { @@ -408,15 +414,18 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface { @override Future acceptShift(String shiftId) async { - await _updateApplicationStatus(shiftId, dc.ApplicationStatus.ACCEPTED); - } - - @override - Future declineShift(String shiftId) async { - await _updateApplicationStatus(shiftId, dc.ApplicationStatus.REJECTED); + await _updateApplicationStatus(shiftId, dc.ApplicationStatus.ACCEPTED); } - Future _updateApplicationStatus(String shiftId, dc.ApplicationStatus newStatus) async { + @override + Future declineShift(String shiftId) async { + await _updateApplicationStatus(shiftId, dc.ApplicationStatus.REJECTED); + } + + Future _updateApplicationStatus( + String shiftId, + dc.ApplicationStatus newStatus, + ) async { String? appId = _shiftToAppIdMap[shiftId]; String? roleId; @@ -427,43 +436,49 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface { // Re-check map appId = _shiftToAppIdMap[shiftId]; if (appId != null) { - roleId = _appToRoleIdMap[appId]; + roleId = _appToRoleIdMap[appId]; } else { - // Fallback fetch - final staffId = await _getStaffId(); - final apps = await _dataConnect.getApplicationsByStaffId(staffId: staffId).execute(); - final app = apps.data.applications.where((a) => a.shiftId == shiftId).firstOrNull; - if (app != null) { - appId = app.id; - roleId = app.shiftRole.id; - } + // Fallback fetch + final staffId = await _getStaffId(); + final apps = await _dataConnect + .getApplicationsByStaffId(staffId: staffId) + .execute(); + final app = apps.data.applications + .where((a) => a.shiftId == shiftId) + .firstOrNull; + if (app != null) { + appId = app.id; + roleId = app.shiftRole.id; + } } if (appId == null || roleId == null) { // If we are rejecting and can't find an application, create one as rejected (declining an available shift) if (newStatus == dc.ApplicationStatus.REJECTED) { - final rolesResult = await _dataConnect.listShiftRolesByShiftId(shiftId: shiftId).execute(); - if (rolesResult.data.shiftRoles.isNotEmpty) { - final role = rolesResult.data.shiftRoles.first; - final staffId = await _getStaffId(); - await _dataConnect.createApplication( + final rolesResult = await _dataConnect + .listShiftRolesByShiftId(shiftId: shiftId) + .execute(); + if (rolesResult.data.shiftRoles.isNotEmpty) { + final role = rolesResult.data.shiftRoles.first; + final staffId = await _getStaffId(); + await _dataConnect + .createApplication( shiftId: shiftId, staffId: staffId, roleId: role.id, status: dc.ApplicationStatus.REJECTED, origin: dc.ApplicationOrigin.STAFF, - ).execute(); - return; - } + ) + .execute(); + return; + } } throw Exception("Application not found for shift $shiftId"); } - await _dataConnect.updateApplicationStatus( - id: appId, - roleId: roleId, - ) - .status(newStatus) - .execute(); + await _dataConnect + .updateApplicationStatus(id: appId, roleId: roleId) + .status(newStatus) + .execute(); } } diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/pages/shift_details_page.dart b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/pages/shift_details_page.dart index f8aed5f8..a808e051 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/pages/shift_details_page.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/pages/shift_details_page.dart @@ -123,12 +123,8 @@ class ShiftDetailsPage extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (_) => - Modular.get()..add( - LoadShiftDetailsEvent( - shiftId, - roleId: shift?.roleId, - ), - ), + Modular.get() + ..add(LoadShiftDetailsEvent(shiftId, roleId: shift?.roleId)), child: BlocListener( listener: (context, state) { if (state is ShiftActionSuccess) { @@ -140,12 +136,14 @@ class ShiftDetailsPage extends StatelessWidget { ); Modular.to.navigateToShiftsHome(); } else if (state is ShiftDetailsError) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(state.message), - backgroundColor: const Color(0xFFEF4444), - ), - ); + if (shift == null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(state.message), + backgroundColor: const Color(0xFFEF4444), + ), + ); + } } }, child: BlocBuilder( @@ -180,7 +178,7 @@ class ShiftDetailsPage extends StatelessWidget { appBar: UiAppBar( title: displayShift.title, centerTitle: false, - onLeadingPressed:() => Modular.to.navigateToShiftsHome(), + onLeadingPressed: () => Modular.to.navigateToShiftsHome(), ), body: Column( children: [ @@ -403,14 +401,14 @@ class ShiftDetailsPage extends StatelessWidget { color: UiColors.textPrimary, ), ), - Text( - displayShift.location.isEmpty - ? "TBD" - : displayShift.locationAddress, - style: UiTypography.title1m.copyWith( - color: UiColors.textPrimary, - ), - ), + Text( + displayShift.location.isEmpty + ? "TBD" + : displayShift.locationAddress, + style: UiTypography.title1m.copyWith( + color: UiColors.textPrimary, + ), + ), ], ), ], diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/widgets/tabs/my_shifts_tab.dart b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/widgets/tabs/my_shifts_tab.dart index 653a4a41..4378e689 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/widgets/tabs/my_shifts_tab.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/widgets/tabs/my_shifts_tab.dart @@ -135,10 +135,7 @@ class _MyShiftsTabState extends State { final visibleMyShifts = widget.myShifts.where((s) { try { final date = DateTime.parse(s.date); - return date.isAfter( - weekStartDate.subtract(const Duration(seconds: 1)), - ) && - date.isBefore(weekEndDate.add(const Duration(days: 1))); + return _isSameDay(date, _selectedDate); } catch (_) { return false; }