diff --git a/apps/mobile/packages/domain/lib/src/entities/shifts/shift.dart b/apps/mobile/packages/domain/lib/src/entities/shifts/shift.dart index fd84d394..8b45cf75 100644 --- a/apps/mobile/packages/domain/lib/src/entities/shifts/shift.dart +++ b/apps/mobile/packages/domain/lib/src/entities/shifts/shift.dart @@ -28,19 +28,36 @@ class Shift extends Equatable { this.clockInMode, this.allowClockInOverride, this.nfcTagId, + this.clientName, + this.roleName, }); /// Deserialises from the V2 API JSON response. + /// + /// Supports both the standard shift JSON shape (`id`, `startsAt`, `endsAt`) + /// and the today-shifts endpoint shape (`shiftId`, `startTime`, `endTime`). factory Shift.fromJson(Map json) { + final String? clientName = json['clientName'] as String?; + final String? roleName = json['roleName'] as String?; + return Shift( - id: json['id'] as String, + id: json['id'] as String? ?? json['shiftId'] as String, orderId: json['orderId'] as String?, - title: json['title'] as String? ?? '', + title: json['title'] as String? ?? + roleName ?? + clientName ?? + '', status: ShiftStatus.fromJson(json['status'] as String?), - startsAt: DateTime.parse(json['startsAt'] as String), - endsAt: DateTime.parse(json['endsAt'] as String), + startsAt: DateTime.parse( + json['startsAt'] as String? ?? json['startTime'] as String, + ), + endsAt: DateTime.parse( + json['endsAt'] as String? ?? json['endTime'] as String, + ), timezone: json['timezone'] as String? ?? 'UTC', - locationName: json['locationName'] as String?, + locationName: json['locationName'] as String? ?? + json['locationAddress'] as String? ?? + json['location'] as String?, locationAddress: json['locationAddress'] as String?, latitude: parseDouble(json['latitude']), longitude: parseDouble(json['longitude']), @@ -51,6 +68,8 @@ class Shift extends Equatable { clockInMode: json['clockInMode'] as String?, allowClockInOverride: json['allowClockInOverride'] as bool?, nfcTagId: json['nfcTagId'] as String?, + clientName: clientName, + roleName: roleName, ); } @@ -108,6 +127,12 @@ class Shift extends Equatable { /// NFC tag identifier for NFC-based clock-in. final String? nfcTagId; + /// Name of the client (business) this shift belongs to. + final String? clientName; + + /// Name of the role the worker is assigned for this shift. + final String? roleName; + /// Serialises to JSON. Map toJson() { return { @@ -129,6 +154,8 @@ class Shift extends Equatable { 'clockInMode': clockInMode, 'allowClockInOverride': allowClockInOverride, 'nfcTagId': nfcTagId, + 'clientName': clientName, + 'roleName': roleName, }; } @@ -161,5 +188,7 @@ class Shift extends Equatable { clockInMode, allowClockInOverride, nfcTagId, + clientName, + roleName, ]; } diff --git a/apps/mobile/packages/features/staff/clock_in/lib/src/data/repositories_impl/clock_in_repository_impl.dart b/apps/mobile/packages/features/staff/clock_in/lib/src/data/repositories_impl/clock_in_repository_impl.dart index ff80e27a..8b903f81 100644 --- a/apps/mobile/packages/features/staff/clock_in/lib/src/data/repositories_impl/clock_in_repository_impl.dart +++ b/apps/mobile/packages/features/staff/clock_in/lib/src/data/repositories_impl/clock_in_repository_impl.dart @@ -26,7 +26,7 @@ class ClockInRepositoryImpl implements ClockInRepositoryInterface { return items .map( (dynamic json) => - _mapTodayShiftJsonToShift(json as Map), + Shift.fromJson(json as Map), ) .toList(); } @@ -58,26 +58,4 @@ class ClockInRepositoryImpl implements ClockInRepositoryInterface { // Re-fetch the attendance status to get the canonical state after clock-out. return getAttendanceStatus(); } - - /// Maps a V2 `listTodayShifts` JSON item to the domain [Shift] entity. - static Shift _mapTodayShiftJsonToShift(Map json) { - return Shift( - id: json['shiftId'] as String, - orderId: null, - title: json['clientName'] as String? ?? json['roleName'] as String? ?? '', - status: ShiftStatus.assigned, - startsAt: DateTime.parse(json['startTime'] as String), - endsAt: DateTime.parse(json['endTime'] as String), - locationName: json['locationAddress'] as String? ?? - json['location'] as String?, - latitude: Shift.parseDouble(json['latitude']), - longitude: Shift.parseDouble(json['longitude']), - geofenceRadiusMeters: json['geofenceRadiusMeters'] as int?, - clockInMode: json['clockInMode'] as String?, - allowClockInOverride: json['allowClockInOverride'] as bool?, - nfcTagId: json['nfcTagId'] as String?, - requiredWorkers: 0, - assignedWorkers: 0, - ); - } } diff --git a/apps/mobile/packages/features/staff/clock_in/lib/src/presentation/widgets/shift_card.dart b/apps/mobile/packages/features/staff/clock_in/lib/src/presentation/widgets/shift_card.dart index fc3aa683..0d5f230f 100644 --- a/apps/mobile/packages/features/staff/clock_in/lib/src/presentation/widgets/shift_card.dart +++ b/apps/mobile/packages/features/staff/clock_in/lib/src/presentation/widgets/shift_card.dart @@ -48,7 +48,13 @@ class ShiftCard extends StatelessWidget { child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded(child: _ShiftDetails(shift: shift, isSelected: isSelected, i18n: i18n)), + Expanded( + child: _ShiftDetails( + shift: shift, + isSelected: isSelected, + i18n: i18n, + ), + ), _ShiftTimeRange(shift: shift), ], ), @@ -76,15 +82,36 @@ class _ShiftDetails extends StatelessWidget { @override Widget build(BuildContext context) { + final String displayTitle = shift.roleName ?? shift.title; + final String? displaySubtitle = shift.clientName; + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(shift.title, style: UiTypography.body2b), - // Currently showing locationName as subtitle fallback. - Text( - shift.locationName ?? '', - style: UiTypography.body3r.textSecondary, - ), + Text(displayTitle, style: UiTypography.body2b), + if (displaySubtitle != null && displaySubtitle.isNotEmpty) + Text(displaySubtitle, style: UiTypography.body3r.textSecondary), + if (shift.locationName != null && shift.locationName!.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: UiConstants.space1), + child: Row( + children: [ + const Icon( + UiIcons.mapPin, + size: 14, + color: UiColors.textSecondary, + ), + const SizedBox(width: UiConstants.space1), + Expanded( + child: Text( + shift.locationName!, + style: UiTypography.body3r.textSecondary, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ), ], ); }