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:
@@ -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'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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',
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -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,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user