feat(shift): enhance Shift entity with client and role names, update JSON deserialization
This commit is contained in:
@@ -28,19 +28,36 @@ class Shift extends Equatable {
|
|||||||
this.clockInMode,
|
this.clockInMode,
|
||||||
this.allowClockInOverride,
|
this.allowClockInOverride,
|
||||||
this.nfcTagId,
|
this.nfcTagId,
|
||||||
|
this.clientName,
|
||||||
|
this.roleName,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Deserialises from the V2 API JSON response.
|
/// 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<String, dynamic> json) {
|
factory Shift.fromJson(Map<String, dynamic> json) {
|
||||||
|
final String? clientName = json['clientName'] as String?;
|
||||||
|
final String? roleName = json['roleName'] as String?;
|
||||||
|
|
||||||
return Shift(
|
return Shift(
|
||||||
id: json['id'] as String,
|
id: json['id'] as String? ?? json['shiftId'] as String,
|
||||||
orderId: json['orderId'] 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?),
|
status: ShiftStatus.fromJson(json['status'] as String?),
|
||||||
startsAt: DateTime.parse(json['startsAt'] as String),
|
startsAt: DateTime.parse(
|
||||||
endsAt: DateTime.parse(json['endsAt'] as String),
|
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',
|
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?,
|
locationAddress: json['locationAddress'] as String?,
|
||||||
latitude: parseDouble(json['latitude']),
|
latitude: parseDouble(json['latitude']),
|
||||||
longitude: parseDouble(json['longitude']),
|
longitude: parseDouble(json['longitude']),
|
||||||
@@ -51,6 +68,8 @@ class Shift extends Equatable {
|
|||||||
clockInMode: json['clockInMode'] as String?,
|
clockInMode: json['clockInMode'] as String?,
|
||||||
allowClockInOverride: json['allowClockInOverride'] as bool?,
|
allowClockInOverride: json['allowClockInOverride'] as bool?,
|
||||||
nfcTagId: json['nfcTagId'] as String?,
|
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.
|
/// NFC tag identifier for NFC-based clock-in.
|
||||||
final String? nfcTagId;
|
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.
|
/// Serialises to JSON.
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return <String, dynamic>{
|
return <String, dynamic>{
|
||||||
@@ -129,6 +154,8 @@ class Shift extends Equatable {
|
|||||||
'clockInMode': clockInMode,
|
'clockInMode': clockInMode,
|
||||||
'allowClockInOverride': allowClockInOverride,
|
'allowClockInOverride': allowClockInOverride,
|
||||||
'nfcTagId': nfcTagId,
|
'nfcTagId': nfcTagId,
|
||||||
|
'clientName': clientName,
|
||||||
|
'roleName': roleName,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,5 +188,7 @@ class Shift extends Equatable {
|
|||||||
clockInMode,
|
clockInMode,
|
||||||
allowClockInOverride,
|
allowClockInOverride,
|
||||||
nfcTagId,
|
nfcTagId,
|
||||||
|
clientName,
|
||||||
|
roleName,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class ClockInRepositoryImpl implements ClockInRepositoryInterface {
|
|||||||
return items
|
return items
|
||||||
.map(
|
.map(
|
||||||
(dynamic json) =>
|
(dynamic json) =>
|
||||||
_mapTodayShiftJsonToShift(json as Map<String, dynamic>),
|
Shift.fromJson(json as Map<String, dynamic>),
|
||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
@@ -58,26 +58,4 @@ class ClockInRepositoryImpl implements ClockInRepositoryInterface {
|
|||||||
// Re-fetch the attendance status to get the canonical state after clock-out.
|
// Re-fetch the attendance status to get the canonical state after clock-out.
|
||||||
return getAttendanceStatus();
|
return getAttendanceStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Maps a V2 `listTodayShifts` JSON item to the domain [Shift] entity.
|
|
||||||
static Shift _mapTodayShiftJsonToShift(Map<String, dynamic> 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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,13 @@ class ShiftCard extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(child: _ShiftDetails(shift: shift, isSelected: isSelected, i18n: i18n)),
|
Expanded(
|
||||||
|
child: _ShiftDetails(
|
||||||
|
shift: shift,
|
||||||
|
isSelected: isSelected,
|
||||||
|
i18n: i18n,
|
||||||
|
),
|
||||||
|
),
|
||||||
_ShiftTimeRange(shift: shift),
|
_ShiftTimeRange(shift: shift),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -76,15 +82,36 @@ class _ShiftDetails extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final String displayTitle = shift.roleName ?? shift.title;
|
||||||
|
final String? displaySubtitle = shift.clientName;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(shift.title, style: UiTypography.body2b),
|
Text(displayTitle, style: UiTypography.body2b),
|
||||||
// Currently showing locationName as subtitle fallback.
|
if (displaySubtitle != null && displaySubtitle.isNotEmpty)
|
||||||
Text(
|
Text(displaySubtitle, style: UiTypography.body3r.textSecondary),
|
||||||
shift.locationName ?? '',
|
if (shift.locationName != null && shift.locationName!.isNotEmpty)
|
||||||
style: UiTypography.body3r.textSecondary,
|
Padding(
|
||||||
),
|
padding: const EdgeInsets.only(top: UiConstants.space1),
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user