feat: enhance shift booking flow with date selection and navigation updates
This commit is contained in:
@@ -28,10 +28,7 @@ class ShiftDetailsBloc extends Bloc<ShiftDetailsEvent, ShiftDetailsState> {
|
|||||||
emit(ShiftDetailsLoading());
|
emit(ShiftDetailsLoading());
|
||||||
try {
|
try {
|
||||||
final shift = await getShiftDetails(
|
final shift = await getShiftDetails(
|
||||||
GetShiftDetailsArguments(
|
GetShiftDetailsArguments(shiftId: event.shiftId, roleId: event.roleId),
|
||||||
shiftId: event.shiftId,
|
|
||||||
roleId: event.roleId,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
if (shift != null) {
|
if (shift != null) {
|
||||||
emit(ShiftDetailsLoaded(shift));
|
emit(ShiftDetailsLoaded(shift));
|
||||||
@@ -53,7 +50,9 @@ class ShiftDetailsBloc extends Bloc<ShiftDetailsEvent, ShiftDetailsState> {
|
|||||||
isInstantBook: true,
|
isInstantBook: true,
|
||||||
roleId: event.roleId,
|
roleId: event.roleId,
|
||||||
);
|
);
|
||||||
emit(const ShiftActionSuccess("Shift successfully booked!"));
|
emit(
|
||||||
|
ShiftActionSuccess("Shift successfully booked!", shiftDate: event.date),
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(ShiftDetailsError(e.toString()));
|
emit(ShiftDetailsError(e.toString()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,11 @@ class LoadShiftDetailsEvent extends ShiftDetailsEvent {
|
|||||||
class BookShiftDetailsEvent extends ShiftDetailsEvent {
|
class BookShiftDetailsEvent extends ShiftDetailsEvent {
|
||||||
final String shiftId;
|
final String shiftId;
|
||||||
final String? roleId;
|
final String? roleId;
|
||||||
const BookShiftDetailsEvent(this.shiftId, {this.roleId});
|
final DateTime? date;
|
||||||
|
const BookShiftDetailsEvent(this.shiftId, {this.roleId, this.date});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [shiftId, roleId];
|
List<Object?> get props => [shiftId, roleId, date];
|
||||||
}
|
}
|
||||||
|
|
||||||
class DeclineShiftDetailsEvent extends ShiftDetailsEvent {
|
class DeclineShiftDetailsEvent extends ShiftDetailsEvent {
|
||||||
|
|||||||
@@ -30,8 +30,9 @@ class ShiftDetailsError extends ShiftDetailsState {
|
|||||||
|
|
||||||
class ShiftActionSuccess extends ShiftDetailsState {
|
class ShiftActionSuccess extends ShiftDetailsState {
|
||||||
final String message;
|
final String message;
|
||||||
const ShiftActionSuccess(this.message);
|
final DateTime? shiftDate;
|
||||||
|
const ShiftActionSuccess(this.message, {this.shiftDate});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [message];
|
List<Object?> get props => [message, shiftDate];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import 'package:flutter_modular/flutter_modular.dart';
|
|||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
|
||||||
extension ShiftsNavigator on IModularNavigator {
|
extension ShiftsNavigator on IModularNavigator {
|
||||||
void navigateToShiftsHome() {
|
void navigateToShiftsHome({DateTime? selectedDate}) {
|
||||||
navigate('/worker-main/shifts/');
|
navigate('/worker-main/shifts/', arguments: {'selectedDate': selectedDate});
|
||||||
}
|
}
|
||||||
|
|
||||||
void pushShiftDetails(Shift shift) {
|
void pushShiftDetails(Shift shift) {
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ class ShiftDetailsPage extends StatelessWidget {
|
|||||||
backgroundColor: const Color(0xFF10B981),
|
backgroundColor: const Color(0xFF10B981),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
Modular.to.navigateToShiftsHome();
|
Modular.to.navigateToShiftsHome(selectedDate: state.shiftDate);
|
||||||
} else if (state is ShiftDetailsError) {
|
} else if (state is ShiftDetailsError) {
|
||||||
if (shift == null) {
|
if (shift == null) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
@@ -473,6 +473,7 @@ class ShiftDetailsPage extends StatelessWidget {
|
|||||||
context,
|
context,
|
||||||
displayShift!.id,
|
displayShift!.id,
|
||||||
displayShift!.roleId,
|
displayShift!.roleId,
|
||||||
|
DateTime.tryParse(displayShift!.date),
|
||||||
),
|
),
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: const Color(0xFF10B981),
|
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(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
@@ -517,7 +523,7 @@ class ShiftDetailsPage extends StatelessWidget {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
BlocProvider.of<ShiftDetailsBloc>(
|
BlocProvider.of<ShiftDetailsBloc>(
|
||||||
context,
|
context,
|
||||||
).add(BookShiftDetailsEvent(id, roleId: roleId));
|
).add(BookShiftDetailsEvent(id, roleId: roleId, date: date));
|
||||||
},
|
},
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor: const Color(0xFF10B981),
|
foregroundColor: const Color(0xFF10B981),
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ import '../styles/shifts_styles.dart';
|
|||||||
|
|
||||||
class ShiftsPage extends StatefulWidget {
|
class ShiftsPage extends StatefulWidget {
|
||||||
final String? initialTab;
|
final String? initialTab;
|
||||||
const ShiftsPage({super.key, this.initialTab});
|
final DateTime? selectedDate;
|
||||||
|
const ShiftsPage({super.key, this.initialTab, this.selectedDate});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ShiftsPage> createState() => _ShiftsPageState();
|
State<ShiftsPage> createState() => _ShiftsPageState();
|
||||||
@@ -19,12 +20,14 @@ class ShiftsPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _ShiftsPageState extends State<ShiftsPage> {
|
class _ShiftsPageState extends State<ShiftsPage> {
|
||||||
late String _activeTab;
|
late String _activeTab;
|
||||||
|
DateTime? _selectedDate;
|
||||||
final ShiftsBloc _bloc = Modular.get<ShiftsBloc>();
|
final ShiftsBloc _bloc = Modular.get<ShiftsBloc>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_activeTab = widget.initialTab ?? 'myshifts';
|
_activeTab = widget.initialTab ?? 'myshifts';
|
||||||
|
_selectedDate = widget.selectedDate;
|
||||||
_bloc.add(LoadShiftsEvent());
|
_bloc.add(LoadShiftsEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,6 +39,11 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
|||||||
_activeTab = widget.initialTab!;
|
_activeTab = widget.initialTab!;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (widget.selectedDate != null && widget.selectedDate != _selectedDate) {
|
||||||
|
setState(() {
|
||||||
|
_selectedDate = widget.selectedDate;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -103,7 +111,8 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
|||||||
"find",
|
"find",
|
||||||
"Find Shifts",
|
"Find Shifts",
|
||||||
UiIcons.search,
|
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),
|
const SizedBox(width: 8),
|
||||||
_buildTab(
|
_buildTab(
|
||||||
@@ -130,8 +139,6 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
|||||||
historyShifts,
|
historyShifts,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -153,15 +160,12 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
|||||||
myShifts: myShifts,
|
myShifts: myShifts,
|
||||||
pendingAssignments: pendingAssignments,
|
pendingAssignments: pendingAssignments,
|
||||||
cancelledShifts: cancelledShifts,
|
cancelledShifts: cancelledShifts,
|
||||||
|
initialDate: _selectedDate,
|
||||||
);
|
);
|
||||||
case 'find':
|
case 'find':
|
||||||
return FindShiftsTab(
|
return FindShiftsTab(availableJobs: availableJobs);
|
||||||
availableJobs: availableJobs,
|
|
||||||
);
|
|
||||||
case 'history':
|
case 'history':
|
||||||
return HistoryShiftsTab(
|
return HistoryShiftsTab(historyShifts: historyShifts);
|
||||||
historyShifts: historyShifts,
|
|
||||||
);
|
|
||||||
default:
|
default:
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,12 +14,14 @@ class MyShiftsTab extends StatefulWidget {
|
|||||||
final List<Shift> myShifts;
|
final List<Shift> myShifts;
|
||||||
final List<Shift> pendingAssignments;
|
final List<Shift> pendingAssignments;
|
||||||
final List<Shift> cancelledShifts;
|
final List<Shift> cancelledShifts;
|
||||||
|
final DateTime? initialDate;
|
||||||
|
|
||||||
const MyShiftsTab({
|
const MyShiftsTab({
|
||||||
super.key,
|
super.key,
|
||||||
required this.myShifts,
|
required this.myShifts,
|
||||||
required this.pendingAssignments,
|
required this.pendingAssignments,
|
||||||
required this.cancelledShifts,
|
required this.cancelledShifts,
|
||||||
|
this.initialDate,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -30,6 +32,45 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
DateTime _selectedDate = DateTime.now();
|
DateTime _selectedDate = DateTime.now();
|
||||||
int _weekOffset = 0;
|
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() {
|
List<DateTime> _getCalendarDays() {
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
int reactDayIndex = now.weekday == 7 ? 0 : now.weekday;
|
int reactDayIndex = now.weekday == 7 ? 0 : now.weekday;
|
||||||
@@ -50,9 +91,7 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: const Text('Accept Shift'),
|
title: const Text('Accept Shift'),
|
||||||
content: const Text(
|
content: const Text('Are you sure you want to accept this shift?'),
|
||||||
'Are you sure you want to accept this shift?',
|
|
||||||
),
|
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
@@ -158,10 +197,7 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
// Calendar Selector
|
// Calendar Selector
|
||||||
Container(
|
Container(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16),
|
||||||
vertical: 16,
|
|
||||||
horizontal: 16,
|
|
||||||
),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
@@ -223,7 +259,9 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
width: 44,
|
width: 44,
|
||||||
height: 60,
|
height: 60,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected ? AppColors.krowBlue : Colors.white,
|
color: isSelected
|
||||||
|
? AppColors.krowBlue
|
||||||
|
: Colors.white,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isSelected
|
color: isSelected
|
||||||
@@ -304,10 +342,7 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
],
|
],
|
||||||
|
|
||||||
if (visibleCancelledShifts.isNotEmpty) ...[
|
if (visibleCancelledShifts.isNotEmpty) ...[
|
||||||
_buildSectionHeader(
|
_buildSectionHeader("Cancelled Shifts", AppColors.krowMuted),
|
||||||
"Cancelled Shifts",
|
|
||||||
AppColors.krowMuted,
|
|
||||||
),
|
|
||||||
...visibleCancelledShifts.map(
|
...visibleCancelledShifts.map(
|
||||||
(shift) => Padding(
|
(shift) => Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 16),
|
padding: const EdgeInsets.only(bottom: 16),
|
||||||
@@ -329,10 +364,7 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
|
|
||||||
// Confirmed Shifts
|
// Confirmed Shifts
|
||||||
if (visibleMyShifts.isNotEmpty) ...[
|
if (visibleMyShifts.isNotEmpty) ...[
|
||||||
_buildSectionHeader(
|
_buildSectionHeader("Confirmed Shifts", AppColors.krowMuted),
|
||||||
"Confirmed Shifts",
|
|
||||||
AppColors.krowMuted,
|
|
||||||
),
|
|
||||||
...visibleMyShifts.map(
|
...visibleMyShifts.map(
|
||||||
(shift) => Padding(
|
(shift) => Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 12),
|
padding: const EdgeInsets.only(bottom: 12),
|
||||||
@@ -349,8 +381,8 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
title: "No shifts this week",
|
title: "No shifts this week",
|
||||||
subtitle: "Try finding new jobs in the Find tab",
|
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({
|
Widget _buildCancelledCard({
|
||||||
required String title,
|
required String title,
|
||||||
required String client,
|
required String client,
|
||||||
|
|||||||
@@ -39,6 +39,15 @@ class StaffShiftsModule extends Module {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void routes(RouteManager r) {
|
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'],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user