refactor: improve code readability and formatting in shifts repository and shift details page

This commit is contained in:
Achintha Isuru
2026-02-01 12:25:45 -05:00
parent 989a1d3f84
commit 5d561ff825
3 changed files with 137 additions and 127 deletions

View File

@@ -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<List<Shift>> getMyShifts() async {
return _fetchApplications(dc.ApplicationStatus.ACCEPTED);
}
@override
Future<List<Shift>> 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<Shift> 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<List<Shift>> 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 <Shift>[];
}
@@ -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<Shift?> getShiftDetails(String shiftId, {String? roleId}) async {
return _getShiftDetails(shiftId, roleId: roleId);
}
Future<Shift?> _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<void> acceptShift(String shiftId) async {
await _updateApplicationStatus(shiftId, dc.ApplicationStatus.ACCEPTED);
}
@override
Future<void> declineShift(String shiftId) async {
await _updateApplicationStatus(shiftId, dc.ApplicationStatus.REJECTED);
await _updateApplicationStatus(shiftId, dc.ApplicationStatus.ACCEPTED);
}
Future<void> _updateApplicationStatus(String shiftId, dc.ApplicationStatus newStatus) async {
@override
Future<void> declineShift(String shiftId) async {
await _updateApplicationStatus(shiftId, dc.ApplicationStatus.REJECTED);
}
Future<void> _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();
}
}

View File

@@ -123,12 +123,8 @@ class ShiftDetailsPage extends StatelessWidget {
Widget build(BuildContext context) {
return BlocProvider<ShiftDetailsBloc>(
create: (_) =>
Modular.get<ShiftDetailsBloc>()..add(
LoadShiftDetailsEvent(
shiftId,
roleId: shift?.roleId,
),
),
Modular.get<ShiftDetailsBloc>()
..add(LoadShiftDetailsEvent(shiftId, roleId: shift?.roleId)),
child: BlocListener<ShiftDetailsBloc, ShiftDetailsState>(
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<ShiftDetailsBloc, ShiftDetailsState>(
@@ -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,
),
),
],
),
],

View File

@@ -135,10 +135,7 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
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;
}