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.
|
||||
- Need to clarify the difference b/w `case dc.ApplicationStatus.ACCEPTED` and `case dc.ApplicationStatus.CONFIRMED`.
|
||||
- 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(
|
||||
ClientHubsIdentifyDialogToggled(hub: hub),
|
||||
),
|
||||
onDeletePressed: () =>
|
||||
BlocProvider.of<ClientHubsBloc>(
|
||||
context,
|
||||
).add(ClientHubsDeleteRequested(hub.id)),
|
||||
onDeletePressed: () => _confirmDeleteHub(
|
||||
context,
|
||||
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
|
||||
UiButton.secondary(
|
||||
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`).
|
||||
void pushShifts({String? tab}) {
|
||||
if (tab == null) {
|
||||
pushNamed('/worker-main/shifts');
|
||||
navigate('/worker-main/shifts');
|
||||
} 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(
|
||||
message: emptyI18n.no_shifts_today,
|
||||
actionLink: emptyI18n.find_shifts_cta,
|
||||
onAction: () =>
|
||||
Modular.to.pushShifts(tab: 'find'),
|
||||
onAction: () => Modular.to.pushShifts(tab: 'find'),
|
||||
)
|
||||
else
|
||||
Column(
|
||||
|
||||
@@ -31,6 +31,7 @@ class ShiftsBloc extends Bloc<ShiftsEvent, ShiftsState> {
|
||||
on<LoadShiftsEvent>(_onLoadShifts);
|
||||
on<LoadHistoryShiftsEvent>(_onLoadHistoryShifts);
|
||||
on<LoadAvailableShiftsEvent>(_onLoadAvailableShifts);
|
||||
on<LoadFindFirstEvent>(_onLoadFindFirst);
|
||||
on<LoadShiftsForRangeEvent>(_onLoadShiftsForRange);
|
||||
on<FilterAvailableShiftsEvent>(_onFilterAvailableShifts);
|
||||
}
|
||||
@@ -62,6 +63,7 @@ class ShiftsBloc extends Bloc<ShiftsEvent, ShiftsState> {
|
||||
availableLoaded: false,
|
||||
historyLoading: false,
|
||||
historyLoaded: false,
|
||||
myShiftsLoaded: true,
|
||||
searchQuery: '',
|
||||
jobType: 'all',
|
||||
));
|
||||
@@ -82,6 +84,7 @@ class ShiftsBloc extends Bloc<ShiftsEvent, ShiftsState> {
|
||||
try {
|
||||
final historyResult = await getHistoryShifts();
|
||||
emit(currentState.copyWith(
|
||||
myShiftsLoaded: true,
|
||||
historyShifts: historyResult,
|
||||
historyLoading: false,
|
||||
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(
|
||||
LoadShiftsForRangeEvent event,
|
||||
Emitter<ShiftsState> emit,
|
||||
@@ -124,7 +188,10 @@ class ShiftsBloc extends Bloc<ShiftsEvent, ShiftsState> {
|
||||
|
||||
if (state is ShiftsLoaded) {
|
||||
final currentState = state as ShiftsLoaded;
|
||||
emit(currentState.copyWith(myShifts: myShiftsResult));
|
||||
emit(currentState.copyWith(
|
||||
myShifts: myShiftsResult,
|
||||
myShiftsLoaded: true,
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -138,6 +205,7 @@ class ShiftsBloc extends Bloc<ShiftsEvent, ShiftsState> {
|
||||
availableLoaded: false,
|
||||
historyLoading: false,
|
||||
historyLoaded: false,
|
||||
myShiftsLoaded: true,
|
||||
searchQuery: '',
|
||||
jobType: 'all',
|
||||
));
|
||||
|
||||
@@ -14,6 +14,8 @@ class LoadHistoryShiftsEvent extends ShiftsEvent {}
|
||||
|
||||
class LoadAvailableShiftsEvent extends ShiftsEvent {}
|
||||
|
||||
class LoadFindFirstEvent extends ShiftsEvent {}
|
||||
|
||||
class LoadShiftsForRangeEvent extends ShiftsEvent {
|
||||
final DateTime start;
|
||||
final DateTime end;
|
||||
|
||||
@@ -22,6 +22,7 @@ class ShiftsLoaded extends ShiftsState {
|
||||
final bool availableLoaded;
|
||||
final bool historyLoading;
|
||||
final bool historyLoaded;
|
||||
final bool myShiftsLoaded;
|
||||
final String searchQuery;
|
||||
final String jobType;
|
||||
|
||||
@@ -35,6 +36,7 @@ class ShiftsLoaded extends ShiftsState {
|
||||
required this.availableLoaded,
|
||||
required this.historyLoading,
|
||||
required this.historyLoaded,
|
||||
required this.myShiftsLoaded,
|
||||
required this.searchQuery,
|
||||
required this.jobType,
|
||||
});
|
||||
@@ -49,6 +51,7 @@ class ShiftsLoaded extends ShiftsState {
|
||||
bool? availableLoaded,
|
||||
bool? historyLoading,
|
||||
bool? historyLoaded,
|
||||
bool? myShiftsLoaded,
|
||||
String? searchQuery,
|
||||
String? jobType,
|
||||
}) {
|
||||
@@ -62,6 +65,7 @@ class ShiftsLoaded extends ShiftsState {
|
||||
availableLoaded: availableLoaded ?? this.availableLoaded,
|
||||
historyLoading: historyLoading ?? this.historyLoading,
|
||||
historyLoaded: historyLoaded ?? this.historyLoaded,
|
||||
myShiftsLoaded: myShiftsLoaded ?? this.myShiftsLoaded,
|
||||
searchQuery: searchQuery ?? this.searchQuery,
|
||||
jobType: jobType ?? this.jobType,
|
||||
);
|
||||
@@ -78,6 +82,7 @@ class ShiftsLoaded extends ShiftsState {
|
||||
availableLoaded,
|
||||
historyLoading,
|
||||
historyLoaded,
|
||||
myShiftsLoaded,
|
||||
searchQuery,
|
||||
jobType,
|
||||
];
|
||||
|
||||
@@ -21,6 +21,7 @@ class ShiftsPage extends StatefulWidget {
|
||||
class _ShiftsPageState extends State<ShiftsPage> {
|
||||
late String _activeTab;
|
||||
DateTime? _selectedDate;
|
||||
bool _prioritizeFind = false;
|
||||
final ShiftsBloc _bloc = Modular.get<ShiftsBloc>();
|
||||
|
||||
@override
|
||||
@@ -28,12 +29,22 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
||||
super.initState();
|
||||
_activeTab = widget.initialTab ?? 'myshifts';
|
||||
_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') {
|
||||
print('ShiftsPage init: loading history tab');
|
||||
_bloc.add(LoadHistoryShiftsEvent());
|
||||
}
|
||||
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) {
|
||||
setState(() {
|
||||
_activeTab = widget.initialTab!;
|
||||
_prioritizeFind = widget.initialTab == 'find';
|
||||
});
|
||||
}
|
||||
if (widget.selectedDate != null && widget.selectedDate != _selectedDate) {
|
||||
@@ -86,6 +98,10 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
||||
final bool historyLoaded = (state is ShiftsLoaded)
|
||||
? state.historyLoaded
|
||||
: false;
|
||||
final bool myShiftsLoaded = (state is ShiftsLoaded)
|
||||
? state.myShiftsLoaded
|
||||
: false;
|
||||
final bool blockTabsForFind = _prioritizeFind && !availableLoaded;
|
||||
|
||||
// Note: "filteredJobs" logic moved to FindShiftsTab
|
||||
// Note: Calendar logic moved to MyShiftsTab
|
||||
@@ -124,7 +140,8 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
||||
"My Shifts",
|
||||
UiIcons.calendar,
|
||||
myShifts.length,
|
||||
enabled: true,
|
||||
showCount: myShiftsLoaded,
|
||||
enabled: !blockTabsForFind,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
_buildTab(
|
||||
@@ -143,7 +160,7 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
||||
UiIcons.clock,
|
||||
historyShifts.length,
|
||||
showCount: historyLoaded,
|
||||
enabled: baseLoaded,
|
||||
enabled: !blockTabsForFind && baseLoaded,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -22,6 +22,12 @@ class _FindShiftsTabState extends State<FindShiftsTab> {
|
||||
String _searchQuery = '';
|
||||
String _jobType = 'all';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
print('FindShiftsTab init: tab entered, data pending');
|
||||
}
|
||||
|
||||
Widget _buildFilterTab(String id, String label) {
|
||||
final isSelected = _jobType == id;
|
||||
return GestureDetector(
|
||||
|
||||
@@ -5,7 +5,7 @@ mutation seedAll @transaction {
|
||||
data: {
|
||||
id: "dvpWnaBjT6UksS5lo04hfMTyq1q1"
|
||||
email: "legendary@krowd.com"
|
||||
fullName: "Krow"
|
||||
fullName: "Krow Payements"
|
||||
role: USER
|
||||
userRole: "BUSINESS"
|
||||
}
|
||||
@@ -26,7 +26,7 @@ mutation seedAll @transaction {
|
||||
id: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
||||
businessName: "Krow"
|
||||
userId: "dvpWnaBjT6UksS5lo04hfMTyq1q1"
|
||||
contactName: "Krow Ops"
|
||||
contactName: "Krow Payements"
|
||||
email: "legendary@krowd.com"
|
||||
phone: "+1-818-555-0148"
|
||||
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
|
||||
|
||||
**Version:** Milestone 3 (v3.0)
|
||||
**Version:** Milestone 3 (v3.0)
|
||||
**Date:** February 3, 2026
|
||||
**Audience:** Business Stakeholders, Customer Engineers, Sales Teams
|
||||
**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 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
|
||||
|
||||
@@ -33,72 +33,78 @@ This demo showcases the progress of the milestone 3.
|
||||
- Client Name: "Krow"
|
||||
|
||||
**Staff Account (Worker):**
|
||||
- Phone: `+1 (555) 765-4321`
|
||||
- Phone: `+15557654321`
|
||||
- OTP Code: `123456` (demo mode)
|
||||
- Name: "Mariana Torres"
|
||||
|
||||
### Prerequisites
|
||||
1. ✅ Both apps installed on demo devices (or simulators)
|
||||
2. ✅ Network connection stable
|
||||
3. ✅ Seed data is ready to be populated
|
||||
- the database should be empty.
|
||||
3. ✅ Seed data is ready to be populated (the database should be empty at start)
|
||||
|
||||
### Pre-Demo Data Seeding
|
||||
### Make Commands Reference
|
||||
|
||||
Tracked in :
|
||||
- https://github.com/Oloodi/krow-workforce/issues/345
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `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.
|
||||
- Commands to use:
|
||||
- `make dataconnect-clean`
|
||||
- To clean the database before seeding.
|
||||
- `make dataconnect-seed`
|
||||
- To populate the database with seed data for demo.
|
||||
### Recent Fixes Applied
|
||||
- ✅ Fixed 2 bugs on TaxForm: marital status and Citizenship Status now properly saved
|
||||
- ✅ Fixed update screen after create or update TaxForm
|
||||
- ✅ Created seed data script
|
||||
- ✅ Created make commands to create and delete information in DataConnect
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
**Purpose:** Demonstrate the starting point before any data exists
|
||||
**Action:** Show the empty database in Firebase console
|
||||
**Purpose:** Demonstrate the starting point before any data exists
|
||||
|
||||
**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)
|
||||
**Purpose:** Show the client onboarding experience and empty states
|
||||
**Purpose:** Show the client onboarding experience and empty states
|
||||
|
||||
**Steps:**
|
||||
1. Open Client App → Tap "Register"
|
||||
2. Enter business email, and password.
|
||||
1. Open Client App → Tap "Create Account"
|
||||
2. Enter business email, and password
|
||||
3. Navigate to home page
|
||||
4. **Point out:** Empty dashboard, no orders, no workers, clean slate
|
||||
|
||||
---
|
||||
|
||||
### 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:**
|
||||
1. Open Staff App → Tap "Register"
|
||||
1. Open Staff App → Tap "Sign Up"
|
||||
2. Enter phone number and verify with OTP code
|
||||
3. Navigate to home page
|
||||
4. **Point out:** Empty shifts list, no available work yet
|
||||
3. Follow the onboarding process
|
||||
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 :
|
||||
- make dataconnect-seed
|
||||
Run the seeding command:
|
||||
```bash
|
||||
make dataconnect-seed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Demo 3: Client Logs In with Existing Account
|
||||
**Purpose:** Show the sign-in experience for returning users
|
||||
**Screen:** Get Started → Sign In
|
||||
**Purpose:** Show the sign-in experience for returning users
|
||||
|
||||
**Screen:** Get Started → Sign In
|
||||
|
||||
**Steps:**
|
||||
1. Restart Client App
|
||||
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
|
||||
**Purpose:** Show how the client app displays active operations
|
||||
**Purpose:** Show how the client app displays active operations
|
||||
|
||||
**Steps:**
|
||||
1. After signing in, observe the home screen
|
||||
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
|
||||
**Screen:** Hubs Tab → "Add Hub" button
|
||||
**Screen:** Hubs Tab → "Add Hub" button
|
||||
|
||||
**Steps:**
|
||||
1. Navigate to Hubs tab in bottom navigation
|
||||
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.
|
||||
> ```
|
||||
>Client Posts Shift [O1]
|
||||
> ↓
|
||||
>*Vendor Accepts the Shift (This Part is missing for now)/ Vendor is selected by client* [O2]
|
||||
> ↓
|
||||
>Worker Searches for a Shift [O3]
|
||||
> ↓
|
||||
>Worker Applies [O4]
|
||||
> ↓
|
||||
>Confirmation (This Part is missing for now, for now is confirmed)*[O5]
|
||||
> ↓
|
||||
>Worker Checks In [O6]
|
||||
> ↓
|
||||
>Shift Completed [O7]
|
||||
>```
|
||||
### 📋 Main Demo Flow Explanation
|
||||
|
||||
```
|
||||
Client Posts Shift [O1]
|
||||
↓
|
||||
*Vendor Accepts the Shift (Missing for now) / Vendor is selected by client* [O2]
|
||||
↓
|
||||
Worker Searches for a Shift [O3]
|
||||
↓
|
||||
Worker Applies [O4]
|
||||
↓
|
||||
Confirmation (Missing for now, auto-confirmed)* [O5]
|
||||
↓
|
||||
Worker Checks In [O6]
|
||||
↓
|
||||
Shift Completed [O7]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Demo 6: Client Creates New Order - [O1]
|
||||
**Purpose:** Walk through the shift creation process
|
||||
**Screen:** Orders Tab → "Post" button
|
||||
**Action:** Create a new shift for upcoming event
|
||||
**Purpose:** Walk through the shift creation process
|
||||
|
||||
**Screen:** Orders Tab → "Post" button
|
||||
|
||||
**What to Fill:**
|
||||
- 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
|
||||
**Purpose:** Show detailed shift information and worker assignments (this second part is missing for now)
|
||||
**Screen:** Orders Tab → Tap on any order card
|
||||
**Action:** Expand order to see full details
|
||||
**Purpose:** Show detailed shift information and worker assignments (second part is missing for now)
|
||||
|
||||
**Screen:** Orders Tab → Tap on any order card
|
||||
|
||||
**What to Notice:**
|
||||
- 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
|
||||
**Purpose:** Show the worker sign-in experience
|
||||
**Screen:** Get Started → Sign In with Phone
|
||||
**Purpose:** Show the worker sign-in experience
|
||||
|
||||
**Screen:** Get Started → Sign In with Phone
|
||||
|
||||
**Steps:**
|
||||
1. Restart the staff app.
|
||||
2. Enter phone number: `5551234567`
|
||||
1. Restart the staff app
|
||||
2. Enter phone number: `5557654321`
|
||||
3. Tap "Send Code"
|
||||
4. Enter OTP: `123456`
|
||||
|
||||
---
|
||||
|
||||
### Demo 9: Staff Views Home Dashboard
|
||||
**Purpose:** Show worker's personalized dashboard
|
||||
**Purpose:** Show worker's personalized dashboard
|
||||
|
||||
**What to Notice:**
|
||||
- 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]
|
||||
**Purpose:** Show how workers discover and view available work
|
||||
**Screen:** Shifts → "Find Work"
|
||||
**Action:** Browse the list of available shifts
|
||||
**Purpose:** Show how workers discover and view available work
|
||||
|
||||
**Screen:** Shifts → "Find Work"
|
||||
|
||||
**What to Notice:**
|
||||
- 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]
|
||||
**Purpose:** Show the application process from worker side
|
||||
**Screen:** Shift Details → "Apply Now" button
|
||||
**Purpose:** Show the application process from worker side
|
||||
|
||||
**Screen:** Shift Details → "Book" Shift button
|
||||
|
||||
**Steps:**
|
||||
1. Tap on an available shift to view details
|
||||
2. Review business name, location, pay, requirements
|
||||
3. Tap "Book Shift"
|
||||
4. See instant confirmation
|
||||
3. Tap "Book" Shift button
|
||||
4. See confirmation
|
||||
|
||||
---
|
||||
|
||||
### Demo 12: Staff Views Confirmed Shifts - [O5]
|
||||
**Purpose:** Show worker's shift management interface
|
||||
**Screen:** Shifts Tab → "My Shifts"
|
||||
**Action:** Review calendar view of confirmed shifts
|
||||
**Purpose:** Show worker's shift management interface
|
||||
|
||||
**Screen:** Shifts Tab → "My Shifts"
|
||||
|
||||
**What to Notice:**
|
||||
- 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]
|
||||
**Purpose:** Show real-time worker tracking capabilities
|
||||
**Screen:** Client App → Coverage Tab
|
||||
**Action:** Navigate to Coverage, select today's date
|
||||
**Purpose:** Show real-time worker tracking capabilities
|
||||
|
||||
**Screen:** Client App → Coverage Tab
|
||||
|
||||
**What to Notice:**
|
||||
- Live worker status (Checked In, En Route, Late, Not Arrived)
|
||||
- Color-coded status badges (green, yellow, red)
|
||||
- Worker contact information
|
||||
- Real-time updates as workers check in
|
||||
- Worker information
|
||||
|
||||
---
|
||||
|
||||
### Demo 14: Staff Checks In to Shift (Day of Event) [O6]
|
||||
**Purpose:** Demonstrate the check-in process
|
||||
**Screen:** Home or My Shifts → Shift Card → "Check In" button
|
||||
**Action:** Simulate checking in to an active shift
|
||||
### Demo 14: Staff Clock-In to Shift (Day of Event) - [O6]
|
||||
**Purpose:** Demonstrate the clock-in process
|
||||
**Screen:** Clockin page → "Clock In" slider
|
||||
|
||||
**What to Notice:**
|
||||
- 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]
|
||||
**Purpose:** Show cross-app interaction and real-time updates
|
||||
**Screen:** Client App → Coverage Tab
|
||||
### Demo 15: Client Sees Clock-In Update - [O6]
|
||||
**Purpose:** Show cross-app interaction and real-time updates
|
||||
|
||||
**Screen:** Client App → Coverage Tab
|
||||
|
||||
**Action:** Press the update button on the top right to refresh worker statuses
|
||||
|
||||
**What to Notice:**
|
||||
- Status update
|
||||
- Green "Checked In" badge appears
|
||||
- User status changes to "Checked In"
|
||||
- Check-in time displayed
|
||||
|
||||
---
|
||||
|
||||
### Demo 16: Staff Checks Out of Shift - - (this is under fixing)[O7]
|
||||
**Purpose:** Demonstrate the check-out process and shift completion
|
||||
**Screen:** Home or My Shifts → Shift Card → "Check Out" button
|
||||
### Demo 16: Staff Clocks-Out of Shift - [O7]
|
||||
**Purpose:** Demonstrate the clocks-out process and shift completion
|
||||
**Screen:** Clockin page -> Clock-out slider
|
||||
|
||||
**What to Notice:**
|
||||
- Check-out timestamp automatically recorded
|
||||
- Clock-out timestamp automatically recorded
|
||||
- Status changes to "Completed"
|
||||
- Total hours worked calculated automatically
|
||||
- Shift moves from active to history
|
||||
|
||||
---
|
||||
|
||||
### Demo 17: Client Views Completed Shift in Coverage - [O7]
|
||||
**Purpose:** Show how completed shifts appear in the client app
|
||||
**Screen:** Client App → Coverage Tab
|
||||
**Purpose:** Show how completed shifts appear in the client app
|
||||
|
||||
**Screen:** Client App → Coverage Tab
|
||||
|
||||
**Action:** Press the refresh button to update worker statuses
|
||||
|
||||
**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
|
||||
**Purpose:** Demonstrate worker profile features and compliance management
|
||||
**Screen:** Staff App → Profile Tab
|
||||
**Purpose:** Demonstrate worker profile features and compliance management
|
||||
|
||||
**Screen:** Staff App → Profile Tab
|
||||
|
||||
**Steps:**
|
||||
1. Navigate to Profile tab in bottom navigation
|
||||
2. Review profile sections:
|
||||
- **Profile Info:** Name, photo, contact details, date of birth
|
||||
- **Statistics:** Total shifts worked, average rating, reliability score
|
||||
- **Profile Info:**
|
||||
- **Emergency Contact:** Name, relationship, phone number
|
||||
- **Bank Account:** Linked payment account for direct deposit
|
||||
- **Certificates:** Food Handler, ServSafe, Background Check status
|
||||
- **Documents:** ID verification, work authorization
|
||||
- **Tax Forms:** W-9, I-9 compliance documents
|
||||
- **Tax Forms:** W-9, I-9 compliance documents *(bugs fixed: marital status and Citizenship Status now work properly)*
|
||||
- **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.
|
||||
- Demo accounts credentials:
|
||||
- Client Account:
|
||||
- Email: `legendary@krowd.com`
|
||||
- Password: `Demo2026!`
|
||||
- Staff Account:
|
||||
- Phone: `+15557654321`
|
||||
- OTP Code: `123456` (demo mode)
|
||||
### Deliverables
|
||||
|
||||
- [ ] Android apps (Client and Staff)
|
||||
- [ ] Demo account credentials (see below)
|
||||
|
||||
### Demo Accounts
|
||||
|
||||
| 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
|
||||
|
||||
launchpad-dev:
|
||||
launchpad-dev: sync-prototypes
|
||||
@echo "--> Starting local Launchpad server using Firebase Hosting emulator..."
|
||||
@echo " - Generating secure email hashes..."
|
||||
@node scripts/generate-allowed-hashes.js
|
||||
@firebase serve --only hosting:launchpad --project=$(FIREBASE_ALIAS)
|
||||
|
||||
deploy-launchpad-hosting:
|
||||
deploy-launchpad-hosting: sync-prototypes
|
||||
@echo "--> Deploying Internal Launchpad to Firebase Hosting..."
|
||||
@echo " - Generating secure email hashes..."
|
||||
@node scripts/generate-allowed-hashes.js
|
||||
|
||||
Reference in New Issue
Block a user