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:
Achintha Isuru
2026-02-03 23:05:35 -05:00
committed by GitHub
14 changed files with 354 additions and 125 deletions

View File

@@ -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.

View File

@@ -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'),
),
],
);
},
);
}
}

View File

@@ -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(),
),
],
),

View File

@@ -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,
});
}
}

View File

@@ -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(

View File

@@ -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',
));

View File

@@ -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;

View File

@@ -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,
];

View File

@@ -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,
),
],
),

View File

@@ -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(

View File

@@ -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
View 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.

View File

@@ -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) |

View File

@@ -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