history ready
This commit is contained in:
@@ -101,7 +101,55 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
||||
|
||||
@override
|
||||
Future<List<Shift>> getHistoryShifts() async {
|
||||
return _fetchApplications(dc.ApplicationStatus.CHECKED_OUT);
|
||||
try {
|
||||
final staffId = await _getStaffId();
|
||||
final response = await _dataConnect
|
||||
.listCompletedApplicationsByStaffId(staffId: staffId)
|
||||
.execute();
|
||||
final List<Shift> shifts = [];
|
||||
|
||||
for (final app in response.data.applications) {
|
||||
_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,
|
||||
),
|
||||
);
|
||||
}
|
||||
return shifts;
|
||||
} catch (e) {
|
||||
return <Shift>[];
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<Shift>> _fetchApplications(
|
||||
|
||||
@@ -29,6 +29,7 @@ class ShiftsBloc extends Bloc<ShiftsEvent, ShiftsState> {
|
||||
required this.getHistoryShifts,
|
||||
}) : super(ShiftsInitial()) {
|
||||
on<LoadShiftsEvent>(_onLoadShifts);
|
||||
on<LoadHistoryShiftsEvent>(_onLoadHistoryShifts);
|
||||
on<LoadShiftsForRangeEvent>(_onLoadShiftsForRange);
|
||||
on<FilterAvailableShiftsEvent>(_onFilterAvailableShifts);
|
||||
}
|
||||
@@ -51,7 +52,6 @@ class ShiftsBloc extends Bloc<ShiftsEvent, ShiftsState> {
|
||||
);
|
||||
final pendingResult = await getPendingAssignments();
|
||||
final cancelledResult = await getCancelledShifts();
|
||||
final historyResult = await getHistoryShifts();
|
||||
|
||||
// Initial available with defaults
|
||||
final availableResult = await getAvailableShifts(const GetAvailableShiftsArguments());
|
||||
@@ -61,7 +61,9 @@ class ShiftsBloc extends Bloc<ShiftsEvent, ShiftsState> {
|
||||
pendingShifts: pendingResult,
|
||||
cancelledShifts: cancelledResult,
|
||||
availableShifts: _filterPastShifts(availableResult),
|
||||
historyShifts: historyResult,
|
||||
historyShifts: const [],
|
||||
historyLoading: false,
|
||||
historyLoaded: false,
|
||||
searchQuery: '',
|
||||
jobType: 'all',
|
||||
));
|
||||
@@ -70,6 +72,27 @@ class ShiftsBloc extends Bloc<ShiftsEvent, ShiftsState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onLoadHistoryShifts(
|
||||
LoadHistoryShiftsEvent event,
|
||||
Emitter<ShiftsState> emit,
|
||||
) async {
|
||||
final currentState = state;
|
||||
if (currentState is! ShiftsLoaded) return;
|
||||
if (currentState.historyLoading || currentState.historyLoaded) return;
|
||||
|
||||
emit(currentState.copyWith(historyLoading: true));
|
||||
try {
|
||||
final historyResult = await getHistoryShifts();
|
||||
emit(currentState.copyWith(
|
||||
historyShifts: historyResult,
|
||||
historyLoading: false,
|
||||
historyLoaded: true,
|
||||
));
|
||||
} catch (_) {
|
||||
emit(currentState.copyWith(historyLoading: false));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onLoadShiftsForRange(
|
||||
LoadShiftsForRangeEvent event,
|
||||
Emitter<ShiftsState> emit,
|
||||
@@ -87,7 +110,6 @@ class ShiftsBloc extends Bloc<ShiftsEvent, ShiftsState> {
|
||||
|
||||
final pendingResult = await getPendingAssignments();
|
||||
final cancelledResult = await getCancelledShifts();
|
||||
final historyResult = await getHistoryShifts();
|
||||
final availableResult =
|
||||
await getAvailableShifts(const GetAvailableShiftsArguments());
|
||||
|
||||
@@ -96,7 +118,9 @@ class ShiftsBloc extends Bloc<ShiftsEvent, ShiftsState> {
|
||||
pendingShifts: pendingResult,
|
||||
cancelledShifts: cancelledResult,
|
||||
availableShifts: _filterPastShifts(availableResult),
|
||||
historyShifts: historyResult,
|
||||
historyShifts: const [],
|
||||
historyLoading: false,
|
||||
historyLoaded: false,
|
||||
searchQuery: '',
|
||||
jobType: 'all',
|
||||
));
|
||||
|
||||
@@ -10,6 +10,8 @@ sealed class ShiftsEvent extends Equatable {
|
||||
|
||||
class LoadShiftsEvent extends ShiftsEvent {}
|
||||
|
||||
class LoadHistoryShiftsEvent extends ShiftsEvent {}
|
||||
|
||||
class LoadShiftsForRangeEvent extends ShiftsEvent {
|
||||
final DateTime start;
|
||||
final DateTime end;
|
||||
|
||||
@@ -18,6 +18,8 @@ class ShiftsLoaded extends ShiftsState {
|
||||
final List<Shift> cancelledShifts;
|
||||
final List<Shift> availableShifts;
|
||||
final List<Shift> historyShifts;
|
||||
final bool historyLoading;
|
||||
final bool historyLoaded;
|
||||
final String searchQuery;
|
||||
final String jobType;
|
||||
|
||||
@@ -27,6 +29,8 @@ class ShiftsLoaded extends ShiftsState {
|
||||
required this.cancelledShifts,
|
||||
required this.availableShifts,
|
||||
required this.historyShifts,
|
||||
required this.historyLoading,
|
||||
required this.historyLoaded,
|
||||
required this.searchQuery,
|
||||
required this.jobType,
|
||||
});
|
||||
@@ -37,6 +41,8 @@ class ShiftsLoaded extends ShiftsState {
|
||||
List<Shift>? cancelledShifts,
|
||||
List<Shift>? availableShifts,
|
||||
List<Shift>? historyShifts,
|
||||
bool? historyLoading,
|
||||
bool? historyLoaded,
|
||||
String? searchQuery,
|
||||
String? jobType,
|
||||
}) {
|
||||
@@ -46,6 +52,8 @@ class ShiftsLoaded extends ShiftsState {
|
||||
cancelledShifts: cancelledShifts ?? this.cancelledShifts,
|
||||
availableShifts: availableShifts ?? this.availableShifts,
|
||||
historyShifts: historyShifts ?? this.historyShifts,
|
||||
historyLoading: historyLoading ?? this.historyLoading,
|
||||
historyLoaded: historyLoaded ?? this.historyLoaded,
|
||||
searchQuery: searchQuery ?? this.searchQuery,
|
||||
jobType: jobType ?? this.jobType,
|
||||
);
|
||||
@@ -58,6 +66,8 @@ class ShiftsLoaded extends ShiftsState {
|
||||
cancelledShifts,
|
||||
availableShifts,
|
||||
historyShifts,
|
||||
historyLoading,
|
||||
historyLoaded,
|
||||
searchQuery,
|
||||
jobType,
|
||||
];
|
||||
|
||||
@@ -29,6 +29,9 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
||||
_activeTab = widget.initialTab ?? 'myshifts';
|
||||
_selectedDate = widget.selectedDate;
|
||||
_bloc.add(LoadShiftsEvent());
|
||||
if (_activeTab == 'history') {
|
||||
_bloc.add(LoadHistoryShiftsEvent());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -67,6 +70,12 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
||||
final List<Shift> historyShifts = (state is ShiftsLoaded)
|
||||
? state.historyShifts
|
||||
: [];
|
||||
final bool historyLoading = (state is ShiftsLoaded)
|
||||
? state.historyLoading
|
||||
: false;
|
||||
final bool historyLoaded = (state is ShiftsLoaded)
|
||||
? state.historyLoaded
|
||||
: false;
|
||||
|
||||
// Note: "filteredJobs" logic moved to FindShiftsTab
|
||||
// Note: Calendar logic moved to MyShiftsTab
|
||||
@@ -120,6 +129,7 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
||||
"History",
|
||||
UiIcons.clock,
|
||||
historyShifts.length,
|
||||
showCount: historyLoaded,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -133,11 +143,12 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: _buildTabContent(
|
||||
myShifts,
|
||||
pendingAssignments,
|
||||
cancelledShifts,
|
||||
availableJobs,
|
||||
historyShifts,
|
||||
),
|
||||
pendingAssignments,
|
||||
cancelledShifts,
|
||||
availableJobs,
|
||||
historyShifts,
|
||||
historyLoading,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -153,6 +164,7 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
||||
List<Shift> cancelledShifts,
|
||||
List<Shift> availableJobs,
|
||||
List<Shift> historyShifts,
|
||||
bool historyLoading,
|
||||
) {
|
||||
switch (_activeTab) {
|
||||
case 'myshifts':
|
||||
@@ -165,17 +177,31 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
||||
case 'find':
|
||||
return FindShiftsTab(availableJobs: availableJobs);
|
||||
case 'history':
|
||||
if (historyLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
return HistoryShiftsTab(historyShifts: historyShifts);
|
||||
default:
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildTab(String id, String label, IconData icon, int count) {
|
||||
Widget _buildTab(
|
||||
String id,
|
||||
String label,
|
||||
IconData icon,
|
||||
int count, {
|
||||
bool showCount = true,
|
||||
}) {
|
||||
final isActive = _activeTab == id;
|
||||
return Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () => setState(() => _activeTab = id),
|
||||
onTap: () {
|
||||
setState(() => _activeTab = id);
|
||||
if (id == 'history') {
|
||||
_bloc.add(LoadHistoryShiftsEvent());
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
@@ -205,27 +231,32 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
constraints: const BoxConstraints(minWidth: 18),
|
||||
decoration: BoxDecoration(
|
||||
color: isActive
|
||||
? AppColors.krowBlue.withAlpha((0.1 * 255).round())
|
||||
: Colors.white.withAlpha((0.2 * 255).round()),
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
"$count",
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: isActive ? AppColors.krowBlue : Colors.white,
|
||||
if (showCount) ...[
|
||||
const SizedBox(width: 4),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 6,
|
||||
vertical: 2,
|
||||
),
|
||||
constraints: const BoxConstraints(minWidth: 18),
|
||||
decoration: BoxDecoration(
|
||||
color: isActive
|
||||
? AppColors.krowBlue.withAlpha((0.1 * 255).round())
|
||||
: Colors.white.withAlpha((0.2 * 255).round()),
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
"$count",
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: isActive ? AppColors.krowBlue : Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user