Merge pull request #365 from Oloodi/312-feature-integrate-google-maps-places-autocomplete-for-hub-address-validation
Continuation of the mobile apps developement
This commit is contained in:
@@ -14,3 +14,5 @@
|
|||||||
- line 125 remove redundant location values.
|
- line 125 remove redundant location values.
|
||||||
- Need to clarify the difference b/w `case dc.ApplicationStatus.ACCEPTED` and `case dc.ApplicationStatus.CONFIRMED`.
|
- Need to clarify the difference b/w `case dc.ApplicationStatus.ACCEPTED` and `case dc.ApplicationStatus.CONFIRMED`.
|
||||||
- Update the dataconnect docs.
|
- Update the dataconnect docs.
|
||||||
|
- Track `lat` and `lng` in the staff preferred work locations (for now we are only storing the name).
|
||||||
|
- Remove "Up Next (x)" counter from orders list in client app as it is confusing, becase the tab already has a badge showing the number of the upcoming orders.
|
||||||
|
|||||||
@@ -95,10 +95,10 @@ class ClientHubsPage extends StatelessWidget {
|
|||||||
).add(
|
).add(
|
||||||
ClientHubsIdentifyDialogToggled(hub: hub),
|
ClientHubsIdentifyDialogToggled(hub: hub),
|
||||||
),
|
),
|
||||||
onDeletePressed: () =>
|
onDeletePressed: () => _confirmDeleteHub(
|
||||||
BlocProvider.of<ClientHubsBloc>(
|
context,
|
||||||
context,
|
hub,
|
||||||
).add(ClientHubsDeleteRequested(hub.id)),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -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,12 +29,22 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
_activeTab = widget.initialTab ?? 'myshifts';
|
_activeTab = widget.initialTab ?? 'myshifts';
|
||||||
_selectedDate = widget.selectedDate;
|
_selectedDate = widget.selectedDate;
|
||||||
_bloc.add(LoadShiftsEvent());
|
print('ShiftsPage init: initialTab=$_activeTab');
|
||||||
|
_prioritizeFind = widget.initialTab == 'find';
|
||||||
|
if (_prioritizeFind) {
|
||||||
|
_bloc.add(LoadFindFirstEvent());
|
||||||
|
} else {
|
||||||
|
_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') {
|
||||||
_bloc.add(LoadAvailableShiftsEvent());
|
print('ShiftsPage init: entering find tab (not loaded yet)');
|
||||||
|
if (!_prioritizeFind) {
|
||||||
|
_bloc.add(LoadAvailableShiftsEvent());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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"
|
||||||
|
|||||||
63
demos/m3/m3-notes.md
Normal file
63
demos/m3/m3-notes.md
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# KROW M3 Demo — Test Feedback
|
||||||
|
|
||||||
|
**Date:** February 3, 2026
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Demo 1: Register Business & Show Empty States (Client App)
|
||||||
|
|
||||||
|
- **Flickering company name:** Every time I navigate to the home screen, I see "your company" for a moment before it changes to the real name.
|
||||||
|
- Creating a One-Time Order shows "No Vendors Available" — this is expected, OK.
|
||||||
|
|
||||||
|
**Suggestions: Achintha:**
|
||||||
|
- We need to have a shimmer loading state while fetching data, to avoid flickering and empty states.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Demo 2: Register Staff & Show Empty States (Staff App)
|
||||||
|
|
||||||
|
**Onboarding — Add preferred work locations:**
|
||||||
|
- Suggestion: Use Google Maps to suggest only city names. Currently users can type anything, which will cause misspellings and inconsistent data. Important for the max distance feature.
|
||||||
|
|
||||||
|
**Home page:**
|
||||||
|
- Same flickering issue — shows "Krower" briefly before displaying the real name.
|
||||||
|
|
||||||
|
**Profile page:**
|
||||||
|
- Phone number should be read-only, or require re-verification if changed.
|
||||||
|
- Emergency contact: "Save & Continue" works, but shouldn't we navigate to the profile page after? Other flows do this.
|
||||||
|
- Tax Documents: Would be great to add a file uploader where our AI could identify documents and prefill fields.
|
||||||
|
- Bank Account: Need to plan real bank verification (KYC)? Ensure the account is real and belongs to the user. Also, I can list banks but I don't see how to change/switch bank.
|
||||||
|
|
||||||
|
**Home (empty state):**
|
||||||
|
- Clicking "Find shifts →" does nothing. But "Find Shifts" with the search icon works.
|
||||||
|
|
||||||
|
**My Availability:**
|
||||||
|
- Working. Some latency, but OK for now.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Demo 5: Client Creates a New Hub
|
||||||
|
|
||||||
|
- Hub editing feature seems missing — we'll need this for NFC configuration later.
|
||||||
|
- No confirmation before deleting a hub.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Demo 6: Client Creates New Order
|
||||||
|
|
||||||
|
- "Up Next (x)" counter is confusing. I created 2 orders but it shows "Up Next (1)". Sometimes shows 0 when navigating, then back to 1.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Demo 8: Staff Logs In with Existing Account
|
||||||
|
|
||||||
|
- If you accidentally click "Sign Up" with an existing phone number, you get stuck:
|
||||||
|
1. OTP screen shows error: "This user already has a staff profile. Please log in"
|
||||||
|
2. Clicking back → login → same OTP error loop
|
||||||
|
3. Only fix: kill and restart the app
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Demo 10: Staff Browses Available Shifts
|
||||||
|
|
||||||
|
- **Blocker:** I don't see the shift I created as the Client.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# KROW Workforce Platform — Feature Demo Plan for Milestone 3
|
# KROW Workforce Platform — Feature Demo Plan for Milestone 3
|
||||||
|
|
||||||
**Version:** Milestone 3 (v3.0)
|
**Version:** Milestone 3 (v3.0)
|
||||||
**Date:** February 3, 2026
|
**Date:** February 3, 2026
|
||||||
**Audience:** Business Stakeholders, Customer Engineers, Sales Teams
|
**Audience:** Business Stakeholders, Customer Engineers, Sales Teams
|
||||||
**Duration:** 25-30 minutes
|
**Duration:** 25-30 minutes
|
||||||
@@ -15,7 +15,7 @@ This demo showcases the progress of the milestone 3.
|
|||||||
|
|
||||||
- **For Businesses (Client App):** One-time shift creation, worker management, real-time coverage tracking
|
- **For Businesses (Client App):** One-time shift creation, worker management, real-time coverage tracking
|
||||||
- **For Workers (Staff App):** Easy access to available shifts, clock-in and profile management
|
- **For Workers (Staff App):** Easy access to available shifts, clock-in and profile management
|
||||||
- **Complete Workflow:** From shift posting and worker check-in and completion/
|
- **Complete Workflow:** From shift posting and worker check-in and completion
|
||||||
|
|
||||||
### Estimated Demo Duration
|
### Estimated Demo Duration
|
||||||
|
|
||||||
@@ -33,72 +33,78 @@ This demo showcases the progress of the milestone 3.
|
|||||||
- Client Name: "Krow"
|
- Client Name: "Krow"
|
||||||
|
|
||||||
**Staff Account (Worker):**
|
**Staff Account (Worker):**
|
||||||
- Phone: `+1 (555) 765-4321`
|
- Phone: `+15557654321`
|
||||||
- OTP Code: `123456` (demo mode)
|
- OTP Code: `123456` (demo mode)
|
||||||
- Name: "Mariana Torres"
|
- Name: "Mariana Torres"
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
1. ✅ Both apps installed on demo devices (or simulators)
|
1. ✅ Both apps installed on demo devices (or simulators)
|
||||||
2. ✅ Network connection stable
|
2. ✅ Network connection stable
|
||||||
3. ✅ Seed data is ready to be populated
|
3. ✅ Seed data is ready to be populated (the database should be empty at start)
|
||||||
- the database should be empty.
|
|
||||||
|
|
||||||
### Pre-Demo Data Seeding
|
### Make Commands Reference
|
||||||
|
|
||||||
Tracked in :
|
| Command | Purpose |
|
||||||
- https://github.com/Oloodi/krow-workforce/issues/345
|
|---------|---------|
|
||||||
|
| `make dataconnect-clean` | Clean the database before seeding |
|
||||||
|
| `make dataconnect-seed` | Populate the database with seed data for demo |
|
||||||
|
|
||||||
- At the start the database should be empty.
|
### Recent Fixes Applied
|
||||||
- Commands to use:
|
- ✅ Fixed 2 bugs on TaxForm: marital status and Citizenship Status now properly saved
|
||||||
- `make dataconnect-clean`
|
- ✅ Fixed update screen after create or update TaxForm
|
||||||
- To clean the database before seeding.
|
- ✅ Created seed data script
|
||||||
- `make dataconnect-seed`
|
- ✅ Created make commands to create and delete information in DataConnect
|
||||||
- To populate the database with seed data for demo.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3️⃣ Demo Flows
|
## 3️⃣ Demo Flows
|
||||||
|
|
||||||
**Note:**
|
|
||||||
To start the demo you should clean the database running the next command:
|
|
||||||
- make dataconnect-clean
|
|
||||||
|
|
||||||
### Demo 0: Show Empty Database
|
### Demo 0: Show Empty Database
|
||||||
**Purpose:** Demonstrate the starting point before any data exists
|
**Purpose:** Demonstrate the starting point before any data exists
|
||||||
**Action:** Show the empty database in Firebase console
|
|
||||||
|
**Steps:**
|
||||||
|
1. Run `make dataconnect-clean` to ensure database is empty
|
||||||
|
2. Show the empty database in Firebase console
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Demo 1: Register Business & Show Empty States (Client App)
|
### Demo 1: Register Business & Show Empty States (Client App)
|
||||||
**Purpose:** Show the client onboarding experience and empty states
|
**Purpose:** Show the client onboarding experience and empty states
|
||||||
|
|
||||||
**Steps:**
|
**Steps:**
|
||||||
1. Open Client App → Tap "Register"
|
1. Open Client App → Tap "Create Account"
|
||||||
2. Enter business email, and password.
|
2. Enter business email, and password
|
||||||
3. Navigate to home page
|
3. Navigate to home page
|
||||||
4. **Point out:** Empty dashboard, no orders, no workers, clean slate
|
4. **Point out:** Empty dashboard, no orders, no workers, clean slate
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Demo 2: Register Staff & Show Empty States (Staff App)
|
### Demo 2: Register Staff & Show Empty States (Staff App)
|
||||||
**Purpose:** Show the worker onboarding experience and empty states
|
**Purpose:** Show the worker onboarding experience and empty states
|
||||||
|
|
||||||
**Steps:**
|
**Steps:**
|
||||||
1. Open Staff App → Tap "Register"
|
1. Open Staff App → Tap "Sign Up"
|
||||||
2. Enter phone number and verify with OTP code
|
2. Enter phone number and verify with OTP code
|
||||||
3. Navigate to home page
|
3. Follow the onboarding process
|
||||||
4. **Point out:** Empty shifts list, no available work yet
|
4. Navigate to home page
|
||||||
|
5. **Point out:** Empty shifts list, no available work yet
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
> **🔄 PAUSE HERE:** Populate the database with seed data (run seeding script)
|
### 🔄 PAUSE: Populate Database
|
||||||
|
|
||||||
- Potulate database with the next comand :
|
Run the seeding command:
|
||||||
- make dataconnect-seed
|
```bash
|
||||||
|
make dataconnect-seed
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Demo 3: Client Logs In with Existing Account
|
### Demo 3: Client Logs In with Existing Account
|
||||||
**Purpose:** Show the sign-in experience for returning users
|
**Purpose:** Show the sign-in experience for returning users
|
||||||
**Screen:** Get Started → Sign In
|
|
||||||
|
**Screen:** Get Started → Sign In
|
||||||
|
|
||||||
**Steps:**
|
**Steps:**
|
||||||
1. Restart Client App
|
1. Restart Client App
|
||||||
2. Tap "Sign In" button
|
2. Tap "Sign In" button
|
||||||
@@ -110,7 +116,8 @@ To start the demo you should clean the database running the next command:
|
|||||||
---
|
---
|
||||||
|
|
||||||
### Demo 4: Client Views Populated Dashboard
|
### Demo 4: Client Views Populated Dashboard
|
||||||
**Purpose:** Show how the client app displays active operations
|
**Purpose:** Show how the client app displays active operations
|
||||||
|
|
||||||
**Steps:**
|
**Steps:**
|
||||||
1. After signing in, observe the home screen
|
1. After signing in, observe the home screen
|
||||||
2. Navigate through populated sections:
|
2. Navigate through populated sections:
|
||||||
@@ -127,7 +134,8 @@ To start the demo you should clean the database running the next command:
|
|||||||
---
|
---
|
||||||
|
|
||||||
### Demo 5: Client Creates a New Hub
|
### Demo 5: Client Creates a New Hub
|
||||||
**Screen:** Hubs Tab → "Add Hub" button
|
**Screen:** Hubs Tab → "Add Hub" button
|
||||||
|
|
||||||
**Steps:**
|
**Steps:**
|
||||||
1. Navigate to Hubs tab in bottom navigation
|
1. Navigate to Hubs tab in bottom navigation
|
||||||
2. Tap the "+" or "Add Hub" button
|
2. Tap the "+" or "Add Hub" button
|
||||||
@@ -139,29 +147,30 @@ To start the demo you should clean the database running the next command:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
> **EXPLAIN**: The main demo flow which is the order creation and acceptance flow.
|
### 📋 Main Demo Flow Explanation
|
||||||
> ```
|
|
||||||
>Client Posts Shift [O1]
|
```
|
||||||
> ↓
|
Client Posts Shift [O1]
|
||||||
>*Vendor Accepts the Shift (This Part is missing for now)/ Vendor is selected by client* [O2]
|
↓
|
||||||
> ↓
|
*Vendor Accepts the Shift (Missing for now) / Vendor is selected by client* [O2]
|
||||||
>Worker Searches for a Shift [O3]
|
↓
|
||||||
> ↓
|
Worker Searches for a Shift [O3]
|
||||||
>Worker Applies [O4]
|
↓
|
||||||
> ↓
|
Worker Applies [O4]
|
||||||
>Confirmation (This Part is missing for now, for now is confirmed)*[O5]
|
↓
|
||||||
> ↓
|
Confirmation (Missing for now, auto-confirmed)* [O5]
|
||||||
>Worker Checks In [O6]
|
↓
|
||||||
> ↓
|
Worker Checks In [O6]
|
||||||
>Shift Completed [O7]
|
↓
|
||||||
>```
|
Shift Completed [O7]
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Demo 6: Client Creates New Order - [O1]
|
### Demo 6: Client Creates New Order - [O1]
|
||||||
**Purpose:** Walk through the shift creation process
|
**Purpose:** Walk through the shift creation process
|
||||||
**Screen:** Orders Tab → "Post" button
|
|
||||||
**Action:** Create a new shift for upcoming event
|
**Screen:** Orders Tab → "Post" button
|
||||||
|
|
||||||
**What to Fill:**
|
**What to Fill:**
|
||||||
- Order name: "Spring Gala 2026"
|
- Order name: "Spring Gala 2026"
|
||||||
@@ -172,9 +181,9 @@ To start the demo you should clean the database running the next command:
|
|||||||
---
|
---
|
||||||
|
|
||||||
### Demo 7: Client Views Order Details
|
### Demo 7: Client Views Order Details
|
||||||
**Purpose:** Show detailed shift information and worker assignments (this second part is missing for now)
|
**Purpose:** Show detailed shift information and worker assignments (second part is missing for now)
|
||||||
**Screen:** Orders Tab → Tap on any order card
|
|
||||||
**Action:** Expand order to see full details
|
**Screen:** Orders Tab → Tap on any order card
|
||||||
|
|
||||||
**What to Notice:**
|
**What to Notice:**
|
||||||
- Event name and location
|
- Event name and location
|
||||||
@@ -186,18 +195,20 @@ To start the demo you should clean the database running the next command:
|
|||||||
---
|
---
|
||||||
|
|
||||||
### Demo 8: Staff Logs In with Existing Account
|
### Demo 8: Staff Logs In with Existing Account
|
||||||
**Purpose:** Show the worker sign-in experience
|
**Purpose:** Show the worker sign-in experience
|
||||||
**Screen:** Get Started → Sign In with Phone
|
|
||||||
|
**Screen:** Get Started → Sign In with Phone
|
||||||
|
|
||||||
**Steps:**
|
**Steps:**
|
||||||
1. Restart the staff app.
|
1. Restart the staff app
|
||||||
2. Enter phone number: `5551234567`
|
2. Enter phone number: `5557654321`
|
||||||
3. Tap "Send Code"
|
3. Tap "Send Code"
|
||||||
4. Enter OTP: `123456`
|
4. Enter OTP: `123456`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Demo 9: Staff Views Home Dashboard
|
### Demo 9: Staff Views Home Dashboard
|
||||||
**Purpose:** Show worker's personalized dashboard
|
**Purpose:** Show worker's personalized dashboard
|
||||||
|
|
||||||
**What to Notice:**
|
**What to Notice:**
|
||||||
- Today's Shifts section (confirmed shifts for today)
|
- Today's Shifts section (confirmed shifts for today)
|
||||||
@@ -206,9 +217,9 @@ To start the demo you should clean the database running the next command:
|
|||||||
---
|
---
|
||||||
|
|
||||||
### Demo 10: Staff Browses Available Shifts - [O3]
|
### Demo 10: Staff Browses Available Shifts - [O3]
|
||||||
**Purpose:** Show how workers discover and view available work
|
**Purpose:** Show how workers discover and view available work
|
||||||
**Screen:** Shifts → "Find Work"
|
|
||||||
**Action:** Browse the list of available shifts
|
**Screen:** Shifts → "Find Work"
|
||||||
|
|
||||||
**What to Notice:**
|
**What to Notice:**
|
||||||
- List of shifts matching worker skills
|
- List of shifts matching worker skills
|
||||||
@@ -219,20 +230,22 @@ To start the demo you should clean the database running the next command:
|
|||||||
---
|
---
|
||||||
|
|
||||||
### Demo 11: Staff Applies for Shift - [O4]
|
### Demo 11: Staff Applies for Shift - [O4]
|
||||||
**Purpose:** Show the application process from worker side
|
**Purpose:** Show the application process from worker side
|
||||||
**Screen:** Shift Details → "Apply Now" button
|
|
||||||
|
**Screen:** Shift Details → "Book" Shift button
|
||||||
|
|
||||||
**Steps:**
|
**Steps:**
|
||||||
1. Tap on an available shift to view details
|
1. Tap on an available shift to view details
|
||||||
2. Review business name, location, pay, requirements
|
2. Review business name, location, pay, requirements
|
||||||
3. Tap "Book Shift"
|
3. Tap "Book" Shift button
|
||||||
4. See instant confirmation
|
4. See confirmation
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Demo 12: Staff Views Confirmed Shifts - [O5]
|
### Demo 12: Staff Views Confirmed Shifts - [O5]
|
||||||
**Purpose:** Show worker's shift management interface
|
**Purpose:** Show worker's shift management interface
|
||||||
**Screen:** Shifts Tab → "My Shifts"
|
|
||||||
**Action:** Review calendar view of confirmed shifts
|
**Screen:** Shifts Tab → "My Shifts"
|
||||||
|
|
||||||
**What to Notice:**
|
**What to Notice:**
|
||||||
- Week-by-week calendar navigation
|
- Week-by-week calendar navigation
|
||||||
@@ -242,22 +255,20 @@ To start the demo you should clean the database running the next command:
|
|||||||
---
|
---
|
||||||
|
|
||||||
### Demo 13: Client Monitors Coverage Dashboard - [O5]
|
### Demo 13: Client Monitors Coverage Dashboard - [O5]
|
||||||
**Purpose:** Show real-time worker tracking capabilities
|
**Purpose:** Show real-time worker tracking capabilities
|
||||||
**Screen:** Client App → Coverage Tab
|
|
||||||
**Action:** Navigate to Coverage, select today's date
|
**Screen:** Client App → Coverage Tab
|
||||||
|
|
||||||
**What to Notice:**
|
**What to Notice:**
|
||||||
- Live worker status (Checked In, En Route, Late, Not Arrived)
|
- Live worker status (Checked In, En Route, Late, Not Arrived)
|
||||||
- Color-coded status badges (green, yellow, red)
|
- Color-coded status badges (green, yellow, red)
|
||||||
- Worker contact information
|
- Worker information
|
||||||
- Real-time updates as workers check in
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Demo 14: Staff Checks In to Shift (Day of Event) [O6]
|
### Demo 14: Staff Clock-In to Shift (Day of Event) - [O6]
|
||||||
**Purpose:** Demonstrate the check-in process
|
**Purpose:** Demonstrate the clock-in process
|
||||||
**Screen:** Home or My Shifts → Shift Card → "Check In" button
|
**Screen:** Clockin page → "Clock In" slider
|
||||||
**Action:** Simulate checking in to an active shift
|
|
||||||
|
|
||||||
**What to Notice:**
|
**What to Notice:**
|
||||||
- Timestamp automatically recorded
|
- Timestamp automatically recorded
|
||||||
@@ -265,33 +276,36 @@ To start the demo you should clean the database running the next command:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Demo 15: Client Sees Check-In Update - [O6]
|
### Demo 15: Client Sees Clock-In Update - [O6]
|
||||||
**Purpose:** Show cross-app interaction and real-time updates
|
**Purpose:** Show cross-app interaction and real-time updates
|
||||||
**Screen:** Client App → Coverage Tab
|
|
||||||
|
**Screen:** Client App → Coverage Tab
|
||||||
|
|
||||||
**Action:** Press the update button on the top right to refresh worker statuses
|
**Action:** Press the update button on the top right to refresh worker statuses
|
||||||
|
|
||||||
**What to Notice:**
|
**What to Notice:**
|
||||||
- Status update
|
- Status update
|
||||||
- Green "Checked In" badge appears
|
- User status changes to "Checked In"
|
||||||
- Check-in time displayed
|
- Check-in time displayed
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Demo 16: Staff Checks Out of Shift - - (this is under fixing)[O7]
|
### Demo 16: Staff Clocks-Out of Shift - [O7]
|
||||||
**Purpose:** Demonstrate the check-out process and shift completion
|
**Purpose:** Demonstrate the clocks-out process and shift completion
|
||||||
**Screen:** Home or My Shifts → Shift Card → "Check Out" button
|
**Screen:** Clockin page -> Clock-out slider
|
||||||
|
|
||||||
**What to Notice:**
|
**What to Notice:**
|
||||||
- Check-out timestamp automatically recorded
|
- Clock-out timestamp automatically recorded
|
||||||
- Status changes to "Completed"
|
- Status changes to "Completed"
|
||||||
- Total hours worked calculated automatically
|
- Total hours worked calculated automatically
|
||||||
- Shift moves from active to history
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Demo 17: Client Views Completed Shift in Coverage - [O7]
|
### Demo 17: Client Views Completed Shift in Coverage - [O7]
|
||||||
**Purpose:** Show how completed shifts appear in the client app
|
**Purpose:** Show how completed shifts appear in the client app
|
||||||
**Screen:** Client App → Coverage Tab
|
|
||||||
|
**Screen:** Client App → Coverage Tab
|
||||||
|
|
||||||
**Action:** Press the refresh button to update worker statuses
|
**Action:** Press the refresh button to update worker statuses
|
||||||
|
|
||||||
**What to Notice:**
|
**What to Notice:**
|
||||||
@@ -304,27 +318,31 @@ To start the demo you should clean the database running the next command:
|
|||||||
---
|
---
|
||||||
|
|
||||||
### Demo 18: Staff Profile Management
|
### Demo 18: Staff Profile Management
|
||||||
**Purpose:** Demonstrate worker profile features and compliance management
|
**Purpose:** Demonstrate worker profile features and compliance management
|
||||||
**Screen:** Staff App → Profile Tab
|
|
||||||
|
**Screen:** Staff App → Profile Tab
|
||||||
|
|
||||||
**Steps:**
|
**Steps:**
|
||||||
1. Navigate to Profile tab in bottom navigation
|
1. Navigate to Profile tab in bottom navigation
|
||||||
2. Review profile sections:
|
2. Review profile sections:
|
||||||
- **Profile Info:** Name, photo, contact details, date of birth
|
- **Profile Info:**
|
||||||
- **Statistics:** Total shifts worked, average rating, reliability score
|
- **Emergency Contact:** Name, relationship, phone number
|
||||||
- **Bank Account:** Linked payment account for direct deposit
|
- **Bank Account:** Linked payment account for direct deposit
|
||||||
- **Certificates:** Food Handler, ServSafe, Background Check status
|
- **Tax Forms:** W-9, I-9 compliance documents *(bugs fixed: marital status and Citizenship Status now work properly)*
|
||||||
- **Documents:** ID verification, work authorization
|
|
||||||
- **Tax Forms:** W-9, I-9 compliance documents
|
|
||||||
- **Time Card:** Historical shift records with hours and earnings
|
- **Time Card:** Historical shift records with hours and earnings
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Things we need to handover to the customer
|
## 4️⃣ Customer Handover Checklist
|
||||||
|
|
||||||
- Android apps of the client and staff.
|
### Deliverables
|
||||||
- Demo accounts credentials:
|
|
||||||
- Client Account:
|
- [ ] Android apps (Client and Staff)
|
||||||
- Email: `legendary@krowd.com`
|
- [ ] Demo account credentials (see below)
|
||||||
- Password: `Demo2026!`
|
|
||||||
- Staff Account:
|
### Demo Accounts
|
||||||
- Phone: `+15557654321`
|
|
||||||
- OTP Code: `123456` (demo mode)
|
| Account | Credentials |
|
||||||
|
|---------|-------------|
|
||||||
|
| **Client** | Email: `legendary@krowd.com` / Password: `Demo2026!` |
|
||||||
|
| **Staff** | Phone: `+15557654321` / OTP: `123456` (demo mode) |
|
||||||
@@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
.PHONY: launchpad-dev deploy-launchpad-hosting
|
.PHONY: launchpad-dev deploy-launchpad-hosting
|
||||||
|
|
||||||
launchpad-dev:
|
launchpad-dev: sync-prototypes
|
||||||
@echo "--> Starting local Launchpad server using Firebase Hosting emulator..."
|
@echo "--> Starting local Launchpad server using Firebase Hosting emulator..."
|
||||||
@echo " - Generating secure email hashes..."
|
@echo " - Generating secure email hashes..."
|
||||||
@node scripts/generate-allowed-hashes.js
|
@node scripts/generate-allowed-hashes.js
|
||||||
@firebase serve --only hosting:launchpad --project=$(FIREBASE_ALIAS)
|
@firebase serve --only hosting:launchpad --project=$(FIREBASE_ALIAS)
|
||||||
|
|
||||||
deploy-launchpad-hosting:
|
deploy-launchpad-hosting: sync-prototypes
|
||||||
@echo "--> Deploying Internal Launchpad to Firebase Hosting..."
|
@echo "--> Deploying Internal Launchpad to Firebase Hosting..."
|
||||||
@echo " - Generating secure email hashes..."
|
@echo " - Generating secure email hashes..."
|
||||||
@node scripts/generate-allowed-hashes.js
|
@node scripts/generate-allowed-hashes.js
|
||||||
|
|||||||
Reference in New Issue
Block a user