diff --git a/apps/mobile/NEXT_SPRINT_TASKS.md b/apps/mobile/NEXT_SPRINT_TASKS.md index babe1f19..d35afb90 100644 --- a/apps/mobile/NEXT_SPRINT_TASKS.md +++ b/apps/mobile/NEXT_SPRINT_TASKS.md @@ -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. diff --git a/apps/mobile/packages/features/client/hubs/lib/src/presentation/pages/client_hubs_page.dart b/apps/mobile/packages/features/client/hubs/lib/src/presentation/pages/client_hubs_page.dart index 85f60930..aa3de3e2 100644 --- a/apps/mobile/packages/features/client/hubs/lib/src/presentation/pages/client_hubs_page.dart +++ b/apps/mobile/packages/features/client/hubs/lib/src/presentation/pages/client_hubs_page.dart @@ -95,10 +95,10 @@ class ClientHubsPage extends StatelessWidget { ).add( ClientHubsIdentifyDialogToggled(hub: hub), ), - onDeletePressed: () => - BlocProvider.of( - context, - ).add(ClientHubsDeleteRequested(hub.id)), + onDeletePressed: () => _confirmDeleteHub( + context, + hub, + ), ), ), ], @@ -221,4 +221,51 @@ class ClientHubsPage extends StatelessWidget { ), ); } + + Future _confirmDeleteHub(BuildContext context, Hub hub) async { + final String hubName = hub.name.isEmpty ? 'this hub' : hub.name; + return showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext dialogContext) { + return AlertDialog( + title: const Text('Confirm Hub Deletion'), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 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: [ + TextButton( + onPressed: () => Modular.to.pop(), + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + BlocProvider.of( + context, + ).add(ClientHubsDeleteRequested(hub.id)); + Modular.to.pop(); + }, + style: TextButton.styleFrom( + foregroundColor: UiColors.destructive, + ), + child: const Text('Delete'), + ), + ], + ); + }, + ); + } } diff --git a/apps/mobile/packages/features/client/settings/lib/src/presentation/widgets/client_settings_page/settings_actions.dart b/apps/mobile/packages/features/client/settings/lib/src/presentation/widgets/client_settings_page/settings_actions.dart index e3e99090..e044d1ec 100644 --- a/apps/mobile/packages/features/client/settings/lib/src/presentation/widgets/client_settings_page/settings_actions.dart +++ b/apps/mobile/packages/features/client/settings/lib/src/presentation/widgets/client_settings_page/settings_actions.dart @@ -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(), ), ], ), diff --git a/apps/mobile/packages/features/staff/home/lib/src/presentation/navigation/home_navigator.dart b/apps/mobile/packages/features/staff/home/lib/src/presentation/navigation/home_navigator.dart index 9774cb07..cd9da6f6 100644 --- a/apps/mobile/packages/features/staff/home/lib/src/presentation/navigation/home_navigator.dart +++ b/apps/mobile/packages/features/staff/home/lib/src/presentation/navigation/home_navigator.dart @@ -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: { + 'initialTab': tab, + }); } } diff --git a/apps/mobile/packages/features/staff/home/lib/src/presentation/pages/worker_home_page.dart b/apps/mobile/packages/features/staff/home/lib/src/presentation/pages/worker_home_page.dart index a9b3f169..777cbf14 100644 --- a/apps/mobile/packages/features/staff/home/lib/src/presentation/pages/worker_home_page.dart +++ b/apps/mobile/packages/features/staff/home/lib/src/presentation/pages/worker_home_page.dart @@ -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( diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shifts/shifts_bloc.dart b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shifts/shifts_bloc.dart index 40ab4f4d..ff8dd4fd 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shifts/shifts_bloc.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shifts/shifts_bloc.dart @@ -31,6 +31,7 @@ class ShiftsBloc extends Bloc { on(_onLoadShifts); on(_onLoadHistoryShifts); on(_onLoadAvailableShifts); + on(_onLoadFindFirst); on(_onLoadShiftsForRange); on(_onFilterAvailableShifts); } @@ -62,6 +63,7 @@ class ShiftsBloc extends Bloc { availableLoaded: false, historyLoading: false, historyLoaded: false, + myShiftsLoaded: true, searchQuery: '', jobType: 'all', )); @@ -82,6 +84,7 @@ class ShiftsBloc extends Bloc { try { final historyResult = await getHistoryShifts(); emit(currentState.copyWith( + myShiftsLoaded: true, historyShifts: historyResult, historyLoading: false, historyLoaded: true, @@ -113,6 +116,67 @@ class ShiftsBloc extends Bloc { } } + Future _onLoadFindFirst( + LoadFindFirstEvent event, + Emitter 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 _onLoadShiftsForRange( LoadShiftsForRangeEvent event, Emitter emit, @@ -124,7 +188,10 @@ class ShiftsBloc extends Bloc { 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 { availableLoaded: false, historyLoading: false, historyLoaded: false, + myShiftsLoaded: true, searchQuery: '', jobType: 'all', )); diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shifts/shifts_event.dart b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shifts/shifts_event.dart index 7822a249..d25866e0 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shifts/shifts_event.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shifts/shifts_event.dart @@ -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; diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shifts/shifts_state.dart b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shifts/shifts_state.dart index dc670b52..d32e3fba 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shifts/shifts_state.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shifts/shifts_state.dart @@ -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, ]; diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/pages/shifts_page.dart b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/pages/shifts_page.dart index f42e6d65..3d86039d 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/pages/shifts_page.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/pages/shifts_page.dart @@ -21,6 +21,7 @@ class ShiftsPage extends StatefulWidget { class _ShiftsPageState extends State { late String _activeTab; DateTime? _selectedDate; + bool _prioritizeFind = false; final ShiftsBloc _bloc = Modular.get(); @override @@ -28,12 +29,22 @@ class _ShiftsPageState extends State { 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 { 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 { 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 { "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 { UiIcons.clock, historyShifts.length, showCount: historyLoaded, - enabled: baseLoaded, + enabled: !blockTabsForFind && baseLoaded, ), ], ), diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/widgets/tabs/find_shifts_tab.dart b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/widgets/tabs/find_shifts_tab.dart index 3bb8c278..4e914b84 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/widgets/tabs/find_shifts_tab.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/widgets/tabs/find_shifts_tab.dart @@ -22,6 +22,12 @@ class _FindShiftsTabState extends State { 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( diff --git a/backend/dataconnect/functions/seed.gql b/backend/dataconnect/functions/seed.gql index f59eda6f..073861ae 100644 --- a/backend/dataconnect/functions/seed.gql +++ b/backend/dataconnect/functions/seed.gql @@ -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" diff --git a/demos/m3/m3-notes.md b/demos/m3/m3-notes.md new file mode 100644 index 00000000..e4111452 --- /dev/null +++ b/demos/m3/m3-notes.md @@ -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. \ No newline at end of file diff --git a/apps/demo plans/MILESTONE 3_DEMO_PLAN.md b/demos/m3/m3.md similarity index 54% rename from apps/demo plans/MILESTONE 3_DEMO_PLAN.md rename to demos/m3/m3.md index 228e0637..31b86d29 100644 --- a/apps/demo plans/MILESTONE 3_DEMO_PLAN.md +++ b/demos/m3/m3.md @@ -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) | diff --git a/makefiles/launchpad.mk b/makefiles/launchpad.mk index 8dbe860f..87050e19 100644 --- a/makefiles/launchpad.mk +++ b/makefiles/launchpad.mk @@ -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