feat: enhance shift booking flow with date selection and navigation updates

This commit is contained in:
Achintha Isuru
2026-02-01 12:35:42 -05:00
parent 5d561ff825
commit 54f28a85ce
8 changed files with 95 additions and 45 deletions

View File

@@ -28,10 +28,7 @@ class ShiftDetailsBloc extends Bloc<ShiftDetailsEvent, ShiftDetailsState> {
emit(ShiftDetailsLoading());
try {
final shift = await getShiftDetails(
GetShiftDetailsArguments(
shiftId: event.shiftId,
roleId: event.roleId,
),
GetShiftDetailsArguments(shiftId: event.shiftId, roleId: event.roleId),
);
if (shift != null) {
emit(ShiftDetailsLoaded(shift));
@@ -53,7 +50,9 @@ class ShiftDetailsBloc extends Bloc<ShiftDetailsEvent, ShiftDetailsState> {
isInstantBook: true,
roleId: event.roleId,
);
emit(const ShiftActionSuccess("Shift successfully booked!"));
emit(
ShiftActionSuccess("Shift successfully booked!", shiftDate: event.date),
);
} catch (e) {
emit(ShiftDetailsError(e.toString()));
}

View File

@@ -19,10 +19,11 @@ class LoadShiftDetailsEvent extends ShiftDetailsEvent {
class BookShiftDetailsEvent extends ShiftDetailsEvent {
final String shiftId;
final String? roleId;
const BookShiftDetailsEvent(this.shiftId, {this.roleId});
final DateTime? date;
const BookShiftDetailsEvent(this.shiftId, {this.roleId, this.date});
@override
List<Object?> get props => [shiftId, roleId];
List<Object?> get props => [shiftId, roleId, date];
}
class DeclineShiftDetailsEvent extends ShiftDetailsEvent {

View File

@@ -30,8 +30,9 @@ class ShiftDetailsError extends ShiftDetailsState {
class ShiftActionSuccess extends ShiftDetailsState {
final String message;
const ShiftActionSuccess(this.message);
final DateTime? shiftDate;
const ShiftActionSuccess(this.message, {this.shiftDate});
@override
List<Object?> get props => [message];
List<Object?> get props => [message, shiftDate];
}

View File

@@ -2,8 +2,8 @@ import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_domain/krow_domain.dart';
extension ShiftsNavigator on IModularNavigator {
void navigateToShiftsHome() {
navigate('/worker-main/shifts/');
void navigateToShiftsHome({DateTime? selectedDate}) {
navigate('/worker-main/shifts/', arguments: {'selectedDate': selectedDate});
}
void pushShiftDetails(Shift shift) {

View File

@@ -134,7 +134,7 @@ class ShiftDetailsPage extends StatelessWidget {
backgroundColor: const Color(0xFF10B981),
),
);
Modular.to.navigateToShiftsHome();
Modular.to.navigateToShiftsHome(selectedDate: state.shiftDate);
} else if (state is ShiftDetailsError) {
if (shift == null) {
ScaffoldMessenger.of(context).showSnackBar(
@@ -473,6 +473,7 @@ class ShiftDetailsPage extends StatelessWidget {
context,
displayShift!.id,
displayShift!.roleId,
DateTime.tryParse(displayShift!.date),
),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF10B981),
@@ -502,7 +503,12 @@ class ShiftDetailsPage extends StatelessWidget {
);
}
void _bookShift(BuildContext context, String id, String? roleId) {
void _bookShift(
BuildContext context,
String id,
String? roleId,
DateTime? date,
) {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
@@ -517,7 +523,7 @@ class ShiftDetailsPage extends StatelessWidget {
onPressed: () {
BlocProvider.of<ShiftDetailsBloc>(
context,
).add(BookShiftDetailsEvent(id, roleId: roleId));
).add(BookShiftDetailsEvent(id, roleId: roleId, date: date));
},
style: TextButton.styleFrom(
foregroundColor: const Color(0xFF10B981),

View File

@@ -11,7 +11,8 @@ import '../styles/shifts_styles.dart';
class ShiftsPage extends StatefulWidget {
final String? initialTab;
const ShiftsPage({super.key, this.initialTab});
final DateTime? selectedDate;
const ShiftsPage({super.key, this.initialTab, this.selectedDate});
@override
State<ShiftsPage> createState() => _ShiftsPageState();
@@ -19,12 +20,14 @@ class ShiftsPage extends StatefulWidget {
class _ShiftsPageState extends State<ShiftsPage> {
late String _activeTab;
DateTime? _selectedDate;
final ShiftsBloc _bloc = Modular.get<ShiftsBloc>();
@override
void initState() {
super.initState();
_activeTab = widget.initialTab ?? 'myshifts';
_selectedDate = widget.selectedDate;
_bloc.add(LoadShiftsEvent());
}
@@ -36,6 +39,11 @@ class _ShiftsPageState extends State<ShiftsPage> {
_activeTab = widget.initialTab!;
});
}
if (widget.selectedDate != null && widget.selectedDate != _selectedDate) {
setState(() {
_selectedDate = widget.selectedDate;
});
}
}
@override
@@ -103,7 +111,8 @@ class _ShiftsPageState extends State<ShiftsPage> {
"find",
"Find Shifts",
UiIcons.search,
availableJobs.length, // Passed unfiltered count as badge? Or logic inside? Pass availableJobs.
availableJobs
.length, // Passed unfiltered count as badge? Or logic inside? Pass availableJobs.
),
const SizedBox(width: 8),
_buildTab(
@@ -130,8 +139,6 @@ class _ShiftsPageState extends State<ShiftsPage> {
historyShifts,
),
),
],
),
);
@@ -153,15 +160,12 @@ class _ShiftsPageState extends State<ShiftsPage> {
myShifts: myShifts,
pendingAssignments: pendingAssignments,
cancelledShifts: cancelledShifts,
initialDate: _selectedDate,
);
case 'find':
return FindShiftsTab(
availableJobs: availableJobs,
);
return FindShiftsTab(availableJobs: availableJobs);
case 'history':
return HistoryShiftsTab(
historyShifts: historyShifts,
);
return HistoryShiftsTab(historyShifts: historyShifts);
default:
return const SizedBox.shrink();
}

View File

@@ -14,12 +14,14 @@ class MyShiftsTab extends StatefulWidget {
final List<Shift> myShifts;
final List<Shift> pendingAssignments;
final List<Shift> cancelledShifts;
final DateTime? initialDate;
const MyShiftsTab({
super.key,
required this.myShifts,
required this.pendingAssignments,
required this.cancelledShifts,
this.initialDate,
});
@override
@@ -30,6 +32,45 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
DateTime _selectedDate = DateTime.now();
int _weekOffset = 0;
@override
void initState() {
super.initState();
if (widget.initialDate != null) {
_applyInitialDate(widget.initialDate!);
}
}
@override
void didUpdateWidget(MyShiftsTab oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.initialDate != null &&
widget.initialDate != oldWidget.initialDate) {
_applyInitialDate(widget.initialDate!);
}
}
void _applyInitialDate(DateTime date) {
_selectedDate = date;
final now = DateTime.now();
int reactDayIndex = now.weekday == 7 ? 0 : now.weekday;
int daysSinceFriday = (reactDayIndex + 2) % 7;
// Base Friday
final baseStart = DateTime(
now.year,
now.month,
now.day,
).subtract(Duration(days: daysSinceFriday));
final target = DateTime(date.year, date.month, date.day);
final diff = target.difference(baseStart).inDays;
setState(() {
_weekOffset = (diff / 7).floor();
});
}
List<DateTime> _getCalendarDays() {
final now = DateTime.now();
int reactDayIndex = now.weekday == 7 ? 0 : now.weekday;
@@ -50,9 +91,7 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
context: context,
builder: (context) => AlertDialog(
title: const Text('Accept Shift'),
content: const Text(
'Are you sure you want to accept this shift?',
),
content: const Text('Are you sure you want to accept this shift?'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
@@ -158,10 +197,7 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
// Calendar Selector
Container(
color: Colors.white,
padding: const EdgeInsets.symmetric(
vertical: 16,
horizontal: 16,
),
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16),
child: Column(
children: [
Padding(
@@ -223,7 +259,9 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
width: 44,
height: 60,
decoration: BoxDecoration(
color: isSelected ? AppColors.krowBlue : Colors.white,
color: isSelected
? AppColors.krowBlue
: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: isSelected
@@ -304,10 +342,7 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
],
if (visibleCancelledShifts.isNotEmpty) ...[
_buildSectionHeader(
"Cancelled Shifts",
AppColors.krowMuted,
),
_buildSectionHeader("Cancelled Shifts", AppColors.krowMuted),
...visibleCancelledShifts.map(
(shift) => Padding(
padding: const EdgeInsets.only(bottom: 16),
@@ -329,10 +364,7 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
// Confirmed Shifts
if (visibleMyShifts.isNotEmpty) ...[
_buildSectionHeader(
"Confirmed Shifts",
AppColors.krowMuted,
),
_buildSectionHeader("Confirmed Shifts", AppColors.krowMuted),
...visibleMyShifts.map(
(shift) => Padding(
padding: const EdgeInsets.only(bottom: 12),
@@ -349,8 +381,8 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
title: "No shifts this week",
subtitle: "Try finding new jobs in the Find tab",
),
const SizedBox(height: UiConstants.space32),
const SizedBox(height: UiConstants.space32),
],
),
),
@@ -385,8 +417,6 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
);
}
Widget _buildCancelledCard({
required String title,
required String client,

View File

@@ -39,6 +39,15 @@ class StaffShiftsModule extends Module {
@override
void routes(RouteManager r) {
r.child('/', child: (_) => const ShiftsPage());
r.child(
'/',
child: (_) {
final args = r.args.data as Map?;
return ShiftsPage(
initialTab: args?['initialTab'],
selectedDate: args?['selectedDate'],
);
},
);
}
}