Merge branch '361-missing-features-and-bugs-identified-during-the-milestone-3-demo-smoke-run' into 312-feature-integrate-google-maps-places-autocomplete-for-hub-address-validation

This commit is contained in:
Achintha Isuru
2026-02-03 22:59:48 -05:00
10 changed files with 162 additions and 16 deletions

View File

@@ -95,10 +95,10 @@ class ClientHubsPage extends StatelessWidget {
).add( ).add(
ClientHubsIdentifyDialogToggled(hub: hub), ClientHubsIdentifyDialogToggled(hub: hub),
), ),
onDeletePressed: () => onDeletePressed: () => _confirmDeleteHub(
BlocProvider.of<ClientHubsBloc>(
context, context,
).add(ClientHubsDeleteRequested(hub.id)), hub,
),
), ),
), ),
], ],
@@ -221,4 +221,51 @@ class ClientHubsPage extends StatelessWidget {
), ),
); );
} }
Future<void> _confirmDeleteHub(BuildContext context, Hub hub) async {
final String hubName = hub.name.isEmpty ? 'this hub' : hub.name;
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext dialogContext) {
return AlertDialog(
title: const Text('Confirm Hub Deletion'),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Are you sure you want to delete "$hubName"?'),
const SizedBox(height: UiConstants.space2),
const Text('This action cannot be undone.'),
const SizedBox(height: UiConstants.space2),
Text(
'Note that if there are any shifts/orders assigned to this hub we shouldn\'t be able to delete the hub.',
style: UiTypography.footnote1r.copyWith(
color: UiColors.textSecondary,
),
),
],
),
actions: <Widget>[
TextButton(
onPressed: () => Modular.to.pop(),
child: const Text('Cancel'),
),
TextButton(
onPressed: () {
BlocProvider.of<ClientHubsBloc>(
context,
).add(ClientHubsDeleteRequested(hub.id));
Modular.to.pop();
},
style: TextButton.styleFrom(
foregroundColor: UiColors.destructive,
),
child: const Text('Delete'),
),
],
);
},
);
}
} }

View File

@@ -83,7 +83,7 @@ class SettingsActions extends StatelessWidget {
// Cancel button // Cancel button
UiButton.secondary( UiButton.secondary(
text: t.common.cancel, text: t.common.cancel,
onPressed: () => Navigator.of(dialogContext).pop(), onPressed: () => Modular.to.pop(),
), ),
], ],
), ),

View File

@@ -31,9 +31,11 @@ extension HomeNavigator on IModularNavigator {
/// Optionally provide a [tab] query param (e.g. `find`). /// Optionally provide a [tab] query param (e.g. `find`).
void pushShifts({String? tab}) { void pushShifts({String? tab}) {
if (tab == null) { if (tab == null) {
pushNamed('/worker-main/shifts'); navigate('/worker-main/shifts');
} else { } else {
pushNamed('/worker-main/shifts?tab=$tab'); navigate('/worker-main/shifts', arguments: <String, dynamic>{
'initialTab': tab,
});
} }
} }

View File

@@ -132,8 +132,7 @@ class WorkerHomePage extends StatelessWidget {
EmptyStateWidget( EmptyStateWidget(
message: emptyI18n.no_shifts_today, message: emptyI18n.no_shifts_today,
actionLink: emptyI18n.find_shifts_cta, actionLink: emptyI18n.find_shifts_cta,
onAction: () => onAction: () => Modular.to.pushShifts(tab: 'find'),
Modular.to.pushShifts(tab: 'find'),
) )
else else
Column( Column(

View File

@@ -31,6 +31,7 @@ class ShiftsBloc extends Bloc<ShiftsEvent, ShiftsState> {
on<LoadShiftsEvent>(_onLoadShifts); on<LoadShiftsEvent>(_onLoadShifts);
on<LoadHistoryShiftsEvent>(_onLoadHistoryShifts); on<LoadHistoryShiftsEvent>(_onLoadHistoryShifts);
on<LoadAvailableShiftsEvent>(_onLoadAvailableShifts); on<LoadAvailableShiftsEvent>(_onLoadAvailableShifts);
on<LoadFindFirstEvent>(_onLoadFindFirst);
on<LoadShiftsForRangeEvent>(_onLoadShiftsForRange); on<LoadShiftsForRangeEvent>(_onLoadShiftsForRange);
on<FilterAvailableShiftsEvent>(_onFilterAvailableShifts); on<FilterAvailableShiftsEvent>(_onFilterAvailableShifts);
} }
@@ -62,6 +63,7 @@ class ShiftsBloc extends Bloc<ShiftsEvent, ShiftsState> {
availableLoaded: false, availableLoaded: false,
historyLoading: false, historyLoading: false,
historyLoaded: false, historyLoaded: false,
myShiftsLoaded: true,
searchQuery: '', searchQuery: '',
jobType: 'all', jobType: 'all',
)); ));
@@ -82,6 +84,7 @@ class ShiftsBloc extends Bloc<ShiftsEvent, ShiftsState> {
try { try {
final historyResult = await getHistoryShifts(); final historyResult = await getHistoryShifts();
emit(currentState.copyWith( emit(currentState.copyWith(
myShiftsLoaded: true,
historyShifts: historyResult, historyShifts: historyResult,
historyLoading: false, historyLoading: false,
historyLoaded: true, historyLoaded: true,
@@ -113,6 +116,67 @@ class ShiftsBloc extends Bloc<ShiftsEvent, ShiftsState> {
} }
} }
Future<void> _onLoadFindFirst(
LoadFindFirstEvent event,
Emitter<ShiftsState> emit,
) async {
if (state is! ShiftsLoaded) {
emit(const ShiftsLoaded(
myShifts: [],
pendingShifts: [],
cancelledShifts: [],
availableShifts: [],
historyShifts: [],
availableLoading: false,
availableLoaded: false,
historyLoading: false,
historyLoaded: false,
myShiftsLoaded: false,
searchQuery: '',
jobType: 'all',
));
}
final currentState =
state is ShiftsLoaded ? state as ShiftsLoaded : null;
if (currentState != null && currentState.availableLoaded) return;
if (currentState != null) {
emit(currentState.copyWith(availableLoading: true));
}
try {
final availableResult =
await getAvailableShifts(const GetAvailableShiftsArguments());
final loadedState = state is ShiftsLoaded
? state as ShiftsLoaded
: const ShiftsLoaded(
myShifts: [],
pendingShifts: [],
cancelledShifts: [],
availableShifts: [],
historyShifts: [],
availableLoading: true,
availableLoaded: false,
historyLoading: false,
historyLoaded: false,
myShiftsLoaded: false,
searchQuery: '',
jobType: 'all',
);
emit(loadedState.copyWith(
availableShifts: _filterPastShifts(availableResult),
availableLoading: false,
availableLoaded: true,
));
} catch (_) {
if (state is ShiftsLoaded) {
final current = state as ShiftsLoaded;
emit(current.copyWith(availableLoading: false));
}
}
}
Future<void> _onLoadShiftsForRange( Future<void> _onLoadShiftsForRange(
LoadShiftsForRangeEvent event, LoadShiftsForRangeEvent event,
Emitter<ShiftsState> emit, Emitter<ShiftsState> emit,
@@ -124,7 +188,10 @@ class ShiftsBloc extends Bloc<ShiftsEvent, ShiftsState> {
if (state is ShiftsLoaded) { if (state is ShiftsLoaded) {
final currentState = state as ShiftsLoaded; final currentState = state as ShiftsLoaded;
emit(currentState.copyWith(myShifts: myShiftsResult)); emit(currentState.copyWith(
myShifts: myShiftsResult,
myShiftsLoaded: true,
));
return; return;
} }
@@ -138,6 +205,7 @@ class ShiftsBloc extends Bloc<ShiftsEvent, ShiftsState> {
availableLoaded: false, availableLoaded: false,
historyLoading: false, historyLoading: false,
historyLoaded: false, historyLoaded: false,
myShiftsLoaded: true,
searchQuery: '', searchQuery: '',
jobType: 'all', jobType: 'all',
)); ));

View File

@@ -14,6 +14,8 @@ class LoadHistoryShiftsEvent extends ShiftsEvent {}
class LoadAvailableShiftsEvent extends ShiftsEvent {} class LoadAvailableShiftsEvent extends ShiftsEvent {}
class LoadFindFirstEvent extends ShiftsEvent {}
class LoadShiftsForRangeEvent extends ShiftsEvent { class LoadShiftsForRangeEvent extends ShiftsEvent {
final DateTime start; final DateTime start;
final DateTime end; final DateTime end;

View File

@@ -22,6 +22,7 @@ class ShiftsLoaded extends ShiftsState {
final bool availableLoaded; final bool availableLoaded;
final bool historyLoading; final bool historyLoading;
final bool historyLoaded; final bool historyLoaded;
final bool myShiftsLoaded;
final String searchQuery; final String searchQuery;
final String jobType; final String jobType;
@@ -35,6 +36,7 @@ class ShiftsLoaded extends ShiftsState {
required this.availableLoaded, required this.availableLoaded,
required this.historyLoading, required this.historyLoading,
required this.historyLoaded, required this.historyLoaded,
required this.myShiftsLoaded,
required this.searchQuery, required this.searchQuery,
required this.jobType, required this.jobType,
}); });
@@ -49,6 +51,7 @@ class ShiftsLoaded extends ShiftsState {
bool? availableLoaded, bool? availableLoaded,
bool? historyLoading, bool? historyLoading,
bool? historyLoaded, bool? historyLoaded,
bool? myShiftsLoaded,
String? searchQuery, String? searchQuery,
String? jobType, String? jobType,
}) { }) {
@@ -62,6 +65,7 @@ class ShiftsLoaded extends ShiftsState {
availableLoaded: availableLoaded ?? this.availableLoaded, availableLoaded: availableLoaded ?? this.availableLoaded,
historyLoading: historyLoading ?? this.historyLoading, historyLoading: historyLoading ?? this.historyLoading,
historyLoaded: historyLoaded ?? this.historyLoaded, historyLoaded: historyLoaded ?? this.historyLoaded,
myShiftsLoaded: myShiftsLoaded ?? this.myShiftsLoaded,
searchQuery: searchQuery ?? this.searchQuery, searchQuery: searchQuery ?? this.searchQuery,
jobType: jobType ?? this.jobType, jobType: jobType ?? this.jobType,
); );
@@ -78,6 +82,7 @@ class ShiftsLoaded extends ShiftsState {
availableLoaded, availableLoaded,
historyLoading, historyLoading,
historyLoaded, historyLoaded,
myShiftsLoaded,
searchQuery, searchQuery,
jobType, jobType,
]; ];

View File

@@ -21,6 +21,7 @@ class ShiftsPage extends StatefulWidget {
class _ShiftsPageState extends State<ShiftsPage> { class _ShiftsPageState extends State<ShiftsPage> {
late String _activeTab; late String _activeTab;
DateTime? _selectedDate; DateTime? _selectedDate;
bool _prioritizeFind = false;
final ShiftsBloc _bloc = Modular.get<ShiftsBloc>(); final ShiftsBloc _bloc = Modular.get<ShiftsBloc>();
@override @override
@@ -28,14 +29,24 @@ class _ShiftsPageState extends State<ShiftsPage> {
super.initState(); super.initState();
_activeTab = widget.initialTab ?? 'myshifts'; _activeTab = widget.initialTab ?? 'myshifts';
_selectedDate = widget.selectedDate; _selectedDate = widget.selectedDate;
print('ShiftsPage init: initialTab=$_activeTab');
_prioritizeFind = widget.initialTab == 'find';
if (_prioritizeFind) {
_bloc.add(LoadFindFirstEvent());
} else {
_bloc.add(LoadShiftsEvent()); _bloc.add(LoadShiftsEvent());
}
if (_activeTab == 'history') { if (_activeTab == 'history') {
print('ShiftsPage init: loading history tab');
_bloc.add(LoadHistoryShiftsEvent()); _bloc.add(LoadHistoryShiftsEvent());
} }
if (_activeTab == 'find') { if (_activeTab == 'find') {
print('ShiftsPage init: entering find tab (not loaded yet)');
if (!_prioritizeFind) {
_bloc.add(LoadAvailableShiftsEvent()); _bloc.add(LoadAvailableShiftsEvent());
} }
} }
}
@override @override
void didUpdateWidget(ShiftsPage oldWidget) { void didUpdateWidget(ShiftsPage oldWidget) {
@@ -43,6 +54,7 @@ class _ShiftsPageState extends State<ShiftsPage> {
if (widget.initialTab != null && widget.initialTab != _activeTab) { if (widget.initialTab != null && widget.initialTab != _activeTab) {
setState(() { setState(() {
_activeTab = widget.initialTab!; _activeTab = widget.initialTab!;
_prioritizeFind = widget.initialTab == 'find';
}); });
} }
if (widget.selectedDate != null && widget.selectedDate != _selectedDate) { if (widget.selectedDate != null && widget.selectedDate != _selectedDate) {
@@ -86,6 +98,10 @@ class _ShiftsPageState extends State<ShiftsPage> {
final bool historyLoaded = (state is ShiftsLoaded) final bool historyLoaded = (state is ShiftsLoaded)
? state.historyLoaded ? state.historyLoaded
: false; : false;
final bool myShiftsLoaded = (state is ShiftsLoaded)
? state.myShiftsLoaded
: false;
final bool blockTabsForFind = _prioritizeFind && !availableLoaded;
// Note: "filteredJobs" logic moved to FindShiftsTab // Note: "filteredJobs" logic moved to FindShiftsTab
// Note: Calendar logic moved to MyShiftsTab // Note: Calendar logic moved to MyShiftsTab
@@ -124,7 +140,8 @@ class _ShiftsPageState extends State<ShiftsPage> {
"My Shifts", "My Shifts",
UiIcons.calendar, UiIcons.calendar,
myShifts.length, myShifts.length,
enabled: true, showCount: myShiftsLoaded,
enabled: !blockTabsForFind,
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
_buildTab( _buildTab(
@@ -143,7 +160,7 @@ class _ShiftsPageState extends State<ShiftsPage> {
UiIcons.clock, UiIcons.clock,
historyShifts.length, historyShifts.length,
showCount: historyLoaded, showCount: historyLoaded,
enabled: baseLoaded, enabled: !blockTabsForFind && baseLoaded,
), ),
], ],
), ),

View File

@@ -22,6 +22,12 @@ class _FindShiftsTabState extends State<FindShiftsTab> {
String _searchQuery = ''; String _searchQuery = '';
String _jobType = 'all'; String _jobType = 'all';
@override
void initState() {
super.initState();
print('FindShiftsTab init: tab entered, data pending');
}
Widget _buildFilterTab(String id, String label) { Widget _buildFilterTab(String id, String label) {
final isSelected = _jobType == id; final isSelected = _jobType == id;
return GestureDetector( return GestureDetector(

View File

@@ -5,7 +5,7 @@ mutation seedAll @transaction {
data: { data: {
id: "dvpWnaBjT6UksS5lo04hfMTyq1q1" id: "dvpWnaBjT6UksS5lo04hfMTyq1q1"
email: "legendary@krowd.com" email: "legendary@krowd.com"
fullName: "Krow" fullName: "Krow Payements"
role: USER role: USER
userRole: "BUSINESS" userRole: "BUSINESS"
} }
@@ -26,7 +26,7 @@ mutation seedAll @transaction {
id: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f" id: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
businessName: "Krow" businessName: "Krow"
userId: "dvpWnaBjT6UksS5lo04hfMTyq1q1" userId: "dvpWnaBjT6UksS5lo04hfMTyq1q1"
contactName: "Krow Ops" contactName: "Krow Payements"
email: "legendary@krowd.com" email: "legendary@krowd.com"
phone: "+1-818-555-0148" phone: "+1-818-555-0148"
address: "5000 San Jose Street, Granada Hills, CA, USA" address: "5000 San Jose Street, Granada Hills, CA, USA"