feat: Add Milestone 3 documentation including feature testing plan, demo notes, and demo flow details
- Introduced comprehensive testing plan for Milestone 3 in m3-client-note.md - Documented feedback and suggestions from demo sessions in m3-notes.md - Created detailed demo flow for Milestone 3 in m3.md, outlining user interactions and expected outcomes - Added planning tasks for Milestone 4 in m4-planning.md, covering backend and frontend development tasks, research, and business tasks
This commit is contained in:
@@ -1,136 +0,0 @@
|
|||||||
# 🎉 Centralized Error Handling - Implementation Complete!
|
|
||||||
|
|
||||||
## ✅ What We Accomplished
|
|
||||||
|
|
||||||
I've successfully implemented a **production-ready centralized error handling system** for both Client and Staff apps. Here's what was delivered:
|
|
||||||
|
|
||||||
### 1. **Core Infrastructure** (100% Complete)
|
|
||||||
**✅ UI Components** (`design_system` package)
|
|
||||||
- `UiErrorSnackbar` - Localized error messages
|
|
||||||
- `UiSuccessSnackbar` - Success feedback
|
|
||||||
|
|
||||||
**✅ BLoC Error Handler Mixin** (`core` package)
|
|
||||||
- `BlocErrorHandler` - Eliminates boilerplate
|
|
||||||
- Automatic error logging
|
|
||||||
- Type-safe error handling
|
|
||||||
|
|
||||||
**✅ Global BLoC Observer** (`core` package)
|
|
||||||
- `CoreBlocObserver` - Centralized monitoring
|
|
||||||
- Registered in both Client and Staff apps
|
|
||||||
- Ready for Sentry/Crashlytics
|
|
||||||
|
|
||||||
**✅ Data Layer Error Handler** (`data_connect` package)
|
|
||||||
- `DataErrorHandler` mixin
|
|
||||||
- Wraps Data Connect calls
|
|
||||||
- Maps `SocketException` and `FirebaseException` to Domain `AppException` (Network/Server)
|
|
||||||
|
|
||||||
### 2. **BLoC Migrations** (2 Complete)
|
|
||||||
**✅ ClientAuthBloc** - 4 event handlers migrated
|
|
||||||
- Reduced from 173 to 153 lines (-11.6%)
|
|
||||||
- Eliminated ~60 lines of boilerplate
|
|
||||||
|
|
||||||
**✅ ClientHubsBloc** - 4 event handlers migrated
|
|
||||||
- Reduced from 232 to 170 lines (-26.7%)
|
|
||||||
- Eliminated ~62 lines of boilerplate
|
|
||||||
|
|
||||||
### 3. **Documentation** (Complete)
|
|
||||||
**✅ 4 comprehensive documents created (now consolidated):**
|
|
||||||
- `CENTRALIZED_ERROR_HANDLING.md` (Architecture guide)
|
|
||||||
- `CENTRALIZED_ERROR_HANDLING_SUMMARY.md` (Implementation summary)
|
|
||||||
- `CENTRALIZED_ERROR_HANDLING_CLIENT_PROPOSAL.md` (Executive summary)
|
|
||||||
- `BLOC_MIGRATION_STATUS.md` (Migration tracking)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 Key Finding
|
|
||||||
|
|
||||||
After analyzing all BLoCs in both apps, I discovered that **most BLoCs don't have error handling yet**. This is actually **good news** because:
|
|
||||||
|
|
||||||
- ✅ **No refactoring needed** - We can use the new pattern from the start
|
|
||||||
- ✅ **Clean implementation** - No legacy error handling to remove
|
|
||||||
- ✅ **Incremental adoption** - Add error handling as needed, not all at once
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 Recommended Approach
|
|
||||||
|
|
||||||
### Option A: Incremental Adoption (Recommended)
|
|
||||||
- Use `BlocErrorHandler` mixin for all **new** BLoCs
|
|
||||||
- Add error handling to **existing** BLoCs as you encounter errors
|
|
||||||
- Focus on user-facing features first
|
|
||||||
- **Estimated effort**: 0-2 hours per BLoC as needed
|
|
||||||
|
|
||||||
### Option B: Complete Migration (Optional)
|
|
||||||
- Migrate all 18 remaining BLoCs now
|
|
||||||
- Add error handling to all event handlers
|
|
||||||
- **Estimated effort**: 15-20 hours total
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 💡 How to Use (For New Development)
|
|
||||||
|
|
||||||
**1. Add the mixin to your BLoC:**
|
|
||||||
```dart
|
|
||||||
class MyBloc extends Bloc<MyEvent, MyState>
|
|
||||||
with BlocErrorHandler<MyState> {
|
|
||||||
```
|
|
||||||
|
|
||||||
**2. Use handleError in event handlers:**
|
|
||||||
```dart
|
|
||||||
await handleError(
|
|
||||||
emit: emit,
|
|
||||||
action: () async {
|
|
||||||
final result = await _useCase();
|
|
||||||
emit(Success(result));
|
|
||||||
},
|
|
||||||
onError: (errorKey) => Error(errorKey),
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
**3. Show errors in UI:**
|
|
||||||
```dart
|
|
||||||
BlocListener<MyBloc, MyState>(
|
|
||||||
listener: (context, state) {
|
|
||||||
if (state.status == Status.error) {
|
|
||||||
UiErrorSnackbar.show(context, messageKey: state.errorMessage!);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📁 Files Created/Modified
|
|
||||||
|
|
||||||
**Created (11 files)**:
|
|
||||||
1. `packages/design_system/lib/src/widgets/ui_error_snackbar.dart`
|
|
||||||
2. `packages/design_system/lib/src/widgets/ui_success_snackbar.dart`
|
|
||||||
3. `packages/core/lib/src/presentation/mixins/bloc_error_handler.dart`
|
|
||||||
4. `packages/core/lib/src/presentation/observers/core_bloc_observer.dart`
|
|
||||||
5. `docs/CENTRALIZED_ERROR_HANDLING.md`
|
|
||||||
6. `docs/CENTRALIZED_ERROR_HANDLING_SUMMARY.md`
|
|
||||||
7. `docs/CENTRALIZED_ERROR_HANDLING_CLIENT_PROPOSAL.md`
|
|
||||||
8. `docs/BLOC_MIGRATION_STATUS.md`
|
|
||||||
|
|
||||||
**Modified (7 files)**:
|
|
||||||
1. `packages/design_system/lib/design_system.dart`
|
|
||||||
2. `packages/core/lib/core.dart`
|
|
||||||
3. `apps/client/lib/main.dart`
|
|
||||||
4. `apps/staff/lib/main.dart`
|
|
||||||
5. `packages/features/client/authentication/lib/src/presentation/blocs/client_auth_bloc.dart`
|
|
||||||
6. `packages/features/client/hubs/lib/src/presentation/blocs/client_hubs_bloc.dart`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✨ Summary
|
|
||||||
|
|
||||||
The centralized error handling system is fully implemented and ready to use for both Client and Staff apps!
|
|
||||||
|
|
||||||
The foundation is solid, the pattern is proven (2 BLoCs migrated successfully), and the documentation is comprehensive. You can now:
|
|
||||||
|
|
||||||
- ✅ **Start using it immediately** for new development
|
|
||||||
- ✅ **Migrate existing BLoCs incrementally** as needed
|
|
||||||
- ✅ **Enjoy consistent error handling** across both apps
|
|
||||||
- ✅ **Reduce boilerplate** by ~20% per BLoC
|
|
||||||
|
|
||||||
**Would you like me to migrate any specific BLoCs now, or are you happy with the incremental approach?**
|
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
flowchart LR
|
||||||
|
|
||||||
|
subgraph C1["Login"]
|
||||||
|
S_client_sign_in["client_sign_in_screen.dart"]
|
||||||
|
S_client_sign_in --> S_client_sign_in_Q["Queries<br/>* user - getUserById<br/>* business - getBusinessesByUserId"]
|
||||||
|
S_client_sign_in --> S_client_sign_in_F["Firebase<br/>* user - auth"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph C2["Create account"]
|
||||||
|
S_client_sign_up["client_sign_up_screen.dart"]
|
||||||
|
S_client_sign_up --> S_client_sign_up_Q["Queries<br/>* business - getBusinessesByUserId"]
|
||||||
|
S_client_sign_up --> S_client_sign_up_M["Mutations<br/>* user - createUser<br/>* business - createBusiness"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph C3["Edit account"]
|
||||||
|
S_edit_account_na["manual_or_unknown_screen.dart"]
|
||||||
|
S_edit_account_na --> S_edit_account_na_M["Mutations<br/>* business - updateBusiness"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph C4["Profile"]
|
||||||
|
S_client_settings["client_settings_screen.dart"]
|
||||||
|
S_client_settings --> S_client_settings_Q["Queries<br/>* user - getUserById<br/>* business - getBusinessesByUserId"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph C5["Hubs"]
|
||||||
|
S_client_hubs["client_hubs_screen.dart"]
|
||||||
|
S_client_hubs --> S_client_hubs_Q["Queries<br/>* TeamHub - listTeamHubsByOwnerId"]
|
||||||
|
S_client_hubs --> S_client_hubs_M["Mutations<br/>* TeamHub - createTeamHub<br/>* TeamHub - updateTeamHub<br/>* TeamHub - deleteTeamHub"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph C6["Orders"]
|
||||||
|
S_client_shifts["client_shifts_screen.dart"]
|
||||||
|
S_client_shifts --> S_client_shifts_Q["Queries<br/>* order - getOrdersByBusinessId"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph C7["RAPID Order"]
|
||||||
|
S_rapid_order["rapid_order_flow_page.dart"]
|
||||||
|
S_rapid_order --> S_rapid_order_M["Mutations<br/>* order - createOrder"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph C8["One-time Order"]
|
||||||
|
S_one_time["one_time_order_flow_page.dart"]
|
||||||
|
S_one_time --> S_one_time_Q["Queries<br/>* role - listRolesByOwnerId"]
|
||||||
|
S_one_time --> S_one_time_M["Mutations<br/>* ShiftRole - createShiftRole<br/>* order - createOrder"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph C9["Permanent Placement"]
|
||||||
|
S_permanent["permanent_order_flow_page.dart"]
|
||||||
|
S_permanent --> S_permanent_Q["Queries<br/>* role - listRolesByOwnerId"]
|
||||||
|
S_permanent --> S_permanent_M["Mutations<br/>* ShiftRole - createShiftRole<br/>* order - createOrder"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph C10["Recurring Order"]
|
||||||
|
S_recurring["recurring_order_flow_page.dart"]
|
||||||
|
S_recurring --> S_recurring_Q["Queries<br/>* role - listRolesByOwnerId"]
|
||||||
|
S_recurring --> S_recurring_M["Mutations<br/>* ShiftRole - createShiftRole<br/>* order - createOrder"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph C11["Billing"]
|
||||||
|
S_billing["client_billing_screen.dart"]
|
||||||
|
S_billing --> S_billing_Q["Queries<br/>* account - getAccountsByOwnerId<br/>* invoice - listInvoicesByBusinessId<br/>* recentPayment - listRecentPaymentsByBusinessId"]
|
||||||
|
S_billing --> S_billing_M["Mutations<br/>* account - createAccount<br/>* account - updateAccount<br/>* account - deleteAccount"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph C12["Coverage"]
|
||||||
|
S_coverage["coverage_dashboard.dart"]
|
||||||
|
S_coverage --> S_coverage_Q["Queries<br/>* order - getOrdersByBusinessId<br/>* shift - getShiftsByBusinessId<br/>* application - getApplicationsByShiftId"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph C13["Home"]
|
||||||
|
S_client_home["client_home_screen.dart"]
|
||||||
|
S_client_home --> S_client_home_Q["Queries<br/>* order - getOrdersByBusinessId<br/>* shift - getShiftsByBusinessId<br/>* application - getApplicationsByShiftId<br/>* recentPayment - listRecentPaymentsByBusinessId"]
|
||||||
|
S_client_home --> S_client_home_M["Mutations<br/>* order - createOrder"]
|
||||||
|
end
|
||||||
121
docs/DATACONNECT_GUIDES/DIAGRAMS/mobile/staff_app_diagram.mmd
Normal file
121
docs/DATACONNECT_GUIDES/DIAGRAMS/mobile/staff_app_diagram.mmd
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
flowchart LR
|
||||||
|
|
||||||
|
subgraph L1["login/create user"]
|
||||||
|
S_auth_phone["phone_verification_screen.dart"]
|
||||||
|
S_auth_phone --> S_auth_phone_Q["Queries<br/>* user - getUserById<br/>* staff - getStaffByUserId"]
|
||||||
|
S_auth_phone --> S_auth_phone_M["Mutations<br/>* user - createUser"]
|
||||||
|
S_auth_phone --> S_auth_phone_F["Firebase<br/>* user - auth"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph L2["Profile"]
|
||||||
|
S_worker_profile["worker_profile_screen.dart"]
|
||||||
|
S_worker_profile --> S_worker_profile_Q["Queries<br/>* user - getUserById<br/>* staff - getStaffByUserId"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph L3["Personal info"]
|
||||||
|
S_personal_info["personal_info_screen.dart"]
|
||||||
|
S_personal_info --> S_personal_info_Q["Queries<br/>* staff - getStaffByUserId"]
|
||||||
|
S_personal_info --> S_personal_info_M["Mutations<br/>* staff - UpdateStaff"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph L4["Emergency Contact"]
|
||||||
|
S_emergency["emergency_contact_screen.dart"]
|
||||||
|
S_emergency --> S_emergency_Q["Queries<br/>* emergencyContact - getEmergencyContactsByStaffId"]
|
||||||
|
S_emergency --> S_emergency_M["Mutations<br/>* conemergencyContacttact - updateEmergencyContact<br/>* emergencyContact - createEmergencyContact<br/>* contemergencyContactact - deleteEmergencyContact"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph L5["Experience & skills"]
|
||||||
|
S_experience["experience_screen.dart"]
|
||||||
|
S_experience --> S_experience_Q["Queries<br/>* staff - getStaffByUserId"]
|
||||||
|
S_experience --> S_experience_M["Mutations<br/>* staff - UpdateStaff"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph L6["Attire"]
|
||||||
|
S_attire["attire_screen.dart"]
|
||||||
|
S_attire --> S_attire_Q["Queries<br/>* attireOption - filterAttireOptions<br/>* staff - getStaffByUserId"]
|
||||||
|
S_attire --> S_attire_M["Mutations<br/>* staff - UpdateStaff"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph L7["Documents"]
|
||||||
|
S_documents["documents_screen.dart"]
|
||||||
|
S_documents --> S_documents_Q["Queries<br/>* document - listDocuments<br/>* staffDocument - listStaffDocumentsByStaffId"]
|
||||||
|
S_documents --> S_documents_M["Mutations<br/>* staffDocument - updateStaffDocument<br/>* staffDocument - createStaffDocument"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph L8["Certificates"]
|
||||||
|
S_certificates["certificates_screen.dart"]
|
||||||
|
S_certificates --> S_certificates_Q["Queries<br/>* certificate - listCertificatesByStaffId"]
|
||||||
|
S_certificates --> S_certificates_M["Mutations<br/>* certificate - UpdateCertificate<br/>* certificate - CreateCertificate<br/>* certificate - DeleteCertificate"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph L9["Tax Documents"]
|
||||||
|
S_tax_forms["tax_forms_screen.dart"]
|
||||||
|
S_tax_forms --> S_tax_forms_Q["Queries<br/>* taxForm - getTaxFormsBystaffId"]
|
||||||
|
S_tax_forms --> S_tax_forms_M["Mutations<br/>* taxForm - createTaxForm<br/>* taxForm - updateTaxForm"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph L10["KROW University"]
|
||||||
|
S_uni["krow_university_screen.dart"]
|
||||||
|
S_uni --> S_uni_Q["Queries<br/>* course - listCourses<br/>* staffCourse - listStaffCoursesByStaffId<br/>* staff - getStaffByUserId<br/>* level - listLevels<br/>* certificate - listCertificatesByStaffId"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph L11["Trainings"]
|
||||||
|
S_trainings["trainings_screen.dart"]
|
||||||
|
S_trainings --> S_trainings_Q["Queries<br/>* course - listCourses<br/>* staffCourse - listStaffCoursesByStaffId"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph L12["Leaderboard"]
|
||||||
|
S_leaderboard["leaderboard_screen.dart"]
|
||||||
|
S_leaderboard --> S_leaderboard_Q["Queries<br/>* staffCourse - missing"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph L13["Bank Account"]
|
||||||
|
S_bank["bank_account_screen.dart"]
|
||||||
|
S_bank --> S_bank_Q["Queries<br/>* account - getAccountsByOwnerId"]
|
||||||
|
S_bank --> S_bank_M["Mutations<br/>* account - createAccount<br/>* account - updateAccount<br/>* account - deleteAccount"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph L14["Earnings/Payments"]
|
||||||
|
S_payments["payments_screen.dart"]
|
||||||
|
S_payments --> S_payments_Q["Queries<br/>* recentPayment - listRecentPaymentsByStaffId"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph L15["Timecard"]
|
||||||
|
S_timecard["time_card_screen.dart"]
|
||||||
|
S_timecard --> S_timecard_Q["Queries<br/>* application - getApplicationsByStaffId"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph L16["Clock in"]
|
||||||
|
S_clockin["clock_in_screen.dart"]
|
||||||
|
S_clockin --> S_clockin_Q["Queries<br/>* application - getApplicationsByStaffId"]
|
||||||
|
S_clockin --> S_clockin_M["Mutations<br/>* application - createApplication<br/>* application - updateApplicationStatus"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph L17["Shifts"]
|
||||||
|
S_shifts["shifts_screen.dart"]
|
||||||
|
S_shifts --> S_shifts_Q["Queries<br/>* application - getApplicationsByStaffId<br/>* shiftRole - listShiftRolesByVendorId/listShiftRolesByRoleId<br/>* application - getApplicationsByStaffId"]
|
||||||
|
S_shifts --> S_shifts_M["Mutations<br/>* application - updateApplicationStatus<br/>* application - createApplication"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph L18["My availability"]
|
||||||
|
S_availability["availability_screen.dart"]
|
||||||
|
S_availability --> S_availability_Q["Queries<br/>* staffAvailability - listStaffAvailabilitiesByStaffId/getStaffAvailabilityByKey"]
|
||||||
|
S_availability --> S_availability_M["Mutations<br/>* staffAvailability - updateStaffAvailability<br/>* staffAvailability - createStaffAvailability<br/>* staffAvailability - deleteStaffAvailability"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph L19["Your Benefits Overview"]
|
||||||
|
S_benefits["benefits_screen.dart"]
|
||||||
|
S_benefits --> S_benefits_Q["Queries<br/>* benefitsData - listBenefitsDataByStaffId"]
|
||||||
|
S_benefits --> S_benefits_M["Mutations<br/>* benefitsData - updateBenefitsData<br/>* benefitsData - createBenefitsData"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph L20["Home"]
|
||||||
|
S_home["worker_home_screen.dart"]
|
||||||
|
S_home --> S_home_Q["Queries<br/>* application - getApplicationsByStaffId<br/>* shiftRole - listShiftRolesByVendorId/listShiftRolesByRoleId<br/>* benefitsData - getBenefitsDataByStaffId"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph L21["Shift detail"]
|
||||||
|
S_shift_detail["shift_details_screen.dart"]
|
||||||
|
S_shift_detail --> S_shift_detail_Q["Queries<br/>* application - getApplicationsByStaffId"]
|
||||||
|
S_shift_detail --> S_shift_detail_M["Mutations<br/>* application - updateApplicationStatus"]
|
||||||
|
end
|
||||||
130
docs/DATACONNECT_GUIDES/DIAGRAMS/uml/business_uml_diagram.mmd
Normal file
130
docs/DATACONNECT_GUIDES/DIAGRAMS/uml/business_uml_diagram.mmd
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
---
|
||||||
|
config:
|
||||||
|
theme: mc
|
||||||
|
layout: dagre
|
||||||
|
---
|
||||||
|
classDiagram
|
||||||
|
direction TB
|
||||||
|
class User {
|
||||||
|
id: String
|
||||||
|
email: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class Business {
|
||||||
|
id: UUID
|
||||||
|
userId: String
|
||||||
|
businessName: String
|
||||||
|
status: BusinessStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
class Vendor {
|
||||||
|
id: UUID
|
||||||
|
userId: String
|
||||||
|
companyName: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class Order {
|
||||||
|
id: UUID
|
||||||
|
businessId: UUID
|
||||||
|
vendorId: UUID
|
||||||
|
status: OrderStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
class Shift {
|
||||||
|
id: UUID
|
||||||
|
orderId: UUID
|
||||||
|
status: ShiftStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShiftRole {
|
||||||
|
shiftId: UUID
|
||||||
|
roleId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
class Role {
|
||||||
|
id: UUID
|
||||||
|
name: String
|
||||||
|
vendorId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
class Application {
|
||||||
|
id: UUID
|
||||||
|
shiftId: UUID
|
||||||
|
staffId: UUID
|
||||||
|
roleId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
class Invoice {
|
||||||
|
id: UUID
|
||||||
|
businessId: UUID
|
||||||
|
vendorId: UUID
|
||||||
|
orderId: UUID
|
||||||
|
status: InvoiceStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
class InvoiceTemplate {
|
||||||
|
id: UUID
|
||||||
|
name: String
|
||||||
|
ownerId: UUID
|
||||||
|
businessId: UUID
|
||||||
|
vendorId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
class RecentPayment {
|
||||||
|
id: UUID
|
||||||
|
invoiceId: UUID
|
||||||
|
applicationId: UUID
|
||||||
|
staffId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClientFeedback {
|
||||||
|
id: UUID
|
||||||
|
businessId: UUID
|
||||||
|
vendorId: UUID
|
||||||
|
rating: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
class Team {
|
||||||
|
id: UUID
|
||||||
|
teamName: String
|
||||||
|
ownerId: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class TeamMember {
|
||||||
|
id: UUID
|
||||||
|
teamId: UUID
|
||||||
|
userId: String
|
||||||
|
role: TeamMemberRole
|
||||||
|
}
|
||||||
|
|
||||||
|
class TeamHub {
|
||||||
|
id: UUID
|
||||||
|
teamId: UUID
|
||||||
|
hubName: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class Staff {
|
||||||
|
id: UUID
|
||||||
|
userId: String
|
||||||
|
}
|
||||||
|
|
||||||
|
note for Staff "business can create a staff too"
|
||||||
|
|
||||||
|
Business "1" -- "*" Order : places
|
||||||
|
Business "1" -- "*" Invoice : receives
|
||||||
|
Business "1" -- "*" ClientFeedback : gives
|
||||||
|
Business "1" --o "1" Team : (ownerId)
|
||||||
|
Business "1" --o "*" InvoiceTemplate : (ownerId)
|
||||||
|
User "1" -- "1" Business : owns
|
||||||
|
Vendor "1" -- "*" Order : fulfills
|
||||||
|
Vendor "1" -- "*" Invoice : issues
|
||||||
|
Order "1" -- "*" Shift : contains
|
||||||
|
Order "1" -- "1" Invoice : billed via
|
||||||
|
Shift "1" -- "*" ShiftRole : requires
|
||||||
|
ShiftRole "1" -- "1" Role
|
||||||
|
ShiftRole "1" -- "*" Application : target for
|
||||||
|
Application "1" -- "1" Staff
|
||||||
|
Application "1" -- "1" RecentPayment
|
||||||
|
Invoice "1" -- "*" RecentPayment : paid through
|
||||||
|
Team "1" -- "*" TeamHub : contains
|
||||||
|
Team "1" -- "*" TeamMember : has
|
||||||
181
docs/DATACONNECT_GUIDES/DIAGRAMS/uml/staff_uml_diagram.mmd
Normal file
181
docs/DATACONNECT_GUIDES/DIAGRAMS/uml/staff_uml_diagram.mmd
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
classDiagram
|
||||||
|
direction TB
|
||||||
|
class User {
|
||||||
|
id: String
|
||||||
|
email: String
|
||||||
|
fullName: String
|
||||||
|
role: UserBaseRole
|
||||||
|
}
|
||||||
|
|
||||||
|
class Staff {
|
||||||
|
id: UUID
|
||||||
|
userId: String
|
||||||
|
fullName: String
|
||||||
|
ownerId: UUID
|
||||||
|
hubId: UUID
|
||||||
|
rollId: UUID
|
||||||
|
status: BackgroundCheckStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
class Account{
|
||||||
|
id: UUID
|
||||||
|
ownerId:UUID
|
||||||
|
type:AccountType
|
||||||
|
}
|
||||||
|
|
||||||
|
class Workforce {
|
||||||
|
id: UUID
|
||||||
|
vendorId: UUID
|
||||||
|
staffId: UUID
|
||||||
|
status: WorkforceStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
class Application {
|
||||||
|
id: UUID
|
||||||
|
shiftId: UUID
|
||||||
|
staffId: UUID
|
||||||
|
roleId: UUID
|
||||||
|
status: ApplicationStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
class Assignment {
|
||||||
|
id: UUID
|
||||||
|
workforceId: UUID
|
||||||
|
shiftId: UUID
|
||||||
|
roleId: UUID
|
||||||
|
status: AssignmentStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShiftRole {
|
||||||
|
id: UUID
|
||||||
|
shiftId: UUID
|
||||||
|
roleId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
class Shift {
|
||||||
|
id: UUID
|
||||||
|
orderId: UUID
|
||||||
|
status: ShiftStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
class Order {
|
||||||
|
id: UUID
|
||||||
|
vendorId: UUID
|
||||||
|
businessId: UUID
|
||||||
|
status: OrderStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
class Vendor {
|
||||||
|
id: UUID
|
||||||
|
userId: String
|
||||||
|
companyName: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class Business {
|
||||||
|
id: UUID
|
||||||
|
userId: String
|
||||||
|
businessName: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class Role {
|
||||||
|
id: UUID
|
||||||
|
name: String
|
||||||
|
vendorId: UUID
|
||||||
|
roleCategoryId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
class StaffDocument {
|
||||||
|
staffId: UUID
|
||||||
|
documentId: UUID
|
||||||
|
status: DocumentStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
class Document {
|
||||||
|
id: UUID
|
||||||
|
name: String
|
||||||
|
documentType: DocumentType
|
||||||
|
}
|
||||||
|
|
||||||
|
class StaffCourse {
|
||||||
|
staffId: UUID
|
||||||
|
courseId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
class Course {
|
||||||
|
id: UUID
|
||||||
|
title: String
|
||||||
|
categoryId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
class Category {
|
||||||
|
id: UUID
|
||||||
|
label: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class StaffAvailability {
|
||||||
|
staffId: UUID
|
||||||
|
day: DayOfWeek
|
||||||
|
slot: AvailabilitySlot
|
||||||
|
}
|
||||||
|
|
||||||
|
class Certificate {
|
||||||
|
staffId: UUID
|
||||||
|
certificationType: ComplianceType
|
||||||
|
status: CertificateStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
class BenefitData {
|
||||||
|
staffId: UUID
|
||||||
|
vendorBenefitPlanId: UUID
|
||||||
|
current: int
|
||||||
|
}
|
||||||
|
|
||||||
|
class Contact {
|
||||||
|
staffId: UUID
|
||||||
|
relationship: RelationshipType
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
class Invoice {
|
||||||
|
orderId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
class RecentPayment {
|
||||||
|
staffId: UUID
|
||||||
|
applicationId: UUID
|
||||||
|
status: RecentPaymentStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
class TaxForm {
|
||||||
|
staffId: UUID
|
||||||
|
formType: TaxFormType
|
||||||
|
status: TaxFormStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
User "1" -- "1" Staff : has
|
||||||
|
Staff "1" -- "*" Application : applies to
|
||||||
|
Staff "1" -- "*" Workforce : part of
|
||||||
|
Staff "1" -- "*" StaffDocument : has
|
||||||
|
Staff "1" -- "*" Certificate : has
|
||||||
|
Staff "1" -- "*" BenefitData : has
|
||||||
|
Staff "1" -- "*" Contact : has
|
||||||
|
Staff "1" -- "*" TaxForm : has
|
||||||
|
Staff "1" -- "*" Account : has
|
||||||
|
Staff "1" -- "*" StaffCourse : takes
|
||||||
|
Staff "1" -- "*" StaffAvailability : sets
|
||||||
|
Workforce "1" -- "*" Assignment : receives
|
||||||
|
Assignment -- ShiftRole
|
||||||
|
Vendor "1" -- "*" Order : receives
|
||||||
|
Business "1" -- "*" Order : places
|
||||||
|
Order "1" -- "1" Invoice : has
|
||||||
|
Invoice "1" -- "*" RecentPayment : has
|
||||||
|
RecentPayment "1" -- "1" Application : has
|
||||||
|
Staff "1" --o "1" Vendor : (ownerId)
|
||||||
|
Staff "1" --o "1" Business : (ownerId)
|
||||||
|
Application -- ShiftRole
|
||||||
|
ShiftRole "*" -- "1" Shift : belongs to
|
||||||
|
ShiftRole "*" -- "1" Role : defines
|
||||||
|
Shift "*" -- "1" Order : belongs to
|
||||||
|
StaffDocument "1" -- "1" Document : references
|
||||||
|
StaffCourse "1" -- "1" Course : references
|
||||||
|
Course "1" -- "1" Category : belongs to
|
||||||
79
docs/DATACONNECT_GUIDES/DIAGRAMS/uml/team_uml_diagram.mmd
Normal file
79
docs/DATACONNECT_GUIDES/DIAGRAMS/uml/team_uml_diagram.mmd
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: elk
|
||||||
|
theme: mc
|
||||||
|
---
|
||||||
|
classDiagram
|
||||||
|
class User {
|
||||||
|
id: String
|
||||||
|
email: String
|
||||||
|
fullName: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class TeamMember {
|
||||||
|
id: UUID
|
||||||
|
teamId: UUID
|
||||||
|
userId: String
|
||||||
|
teamHubId: UUID
|
||||||
|
role: TeamMemberRole
|
||||||
|
inviteStatus: TeamMemberInviteStatus
|
||||||
|
inviteCode: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
class Team {
|
||||||
|
id: UUID
|
||||||
|
teamName: String
|
||||||
|
ownerId: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class TeamHub {
|
||||||
|
id: UUID
|
||||||
|
teamId: UUID
|
||||||
|
hubName: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class TeamHudDepartment {
|
||||||
|
id: UUID
|
||||||
|
name: String
|
||||||
|
teamHubId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
class MemberTask {
|
||||||
|
teamMemberId: UUID
|
||||||
|
taskId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
class Task {
|
||||||
|
id: UUID
|
||||||
|
taskName: String
|
||||||
|
status: TaskStatus
|
||||||
|
priority: TaskPriority
|
||||||
|
ownerId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
class Vendor {
|
||||||
|
id: UUID
|
||||||
|
companyName: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class Business {
|
||||||
|
id: UUID
|
||||||
|
businessName: String
|
||||||
|
}
|
||||||
|
|
||||||
|
User "1" -- "1" TeamMember : has
|
||||||
|
|
||||||
|
Team "1" -- "*" TeamHub : contains
|
||||||
|
Team "1" --o "1" Vendor : (ownerId)
|
||||||
|
Team "1" --o "1" Business : (ownerId)
|
||||||
|
|
||||||
|
TeamHub "1" -- "*" TeamHudDepartment : has
|
||||||
|
TeamHub "1" -- "*" TeamMember : is assigned to
|
||||||
|
|
||||||
|
TeamMember "*" -- "1" Team
|
||||||
|
TeamMember "1" -- "*" MemberTask : has assigned
|
||||||
|
|
||||||
|
Task "1" -- "*" MemberTask : is assigned to
|
||||||
|
Task --o Vendor : (ownerId)
|
||||||
|
Task --o Business : (ownerId)
|
||||||
|
|
||||||
71
docs/DATACONNECT_GUIDES/DIAGRAMS/uml/user_uml_diagram.mmd
Normal file
71
docs/DATACONNECT_GUIDES/DIAGRAMS/uml/user_uml_diagram.mmd
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
|
||||||
|
classDiagram
|
||||||
|
direction TB
|
||||||
|
class User {
|
||||||
|
id: String
|
||||||
|
email: String
|
||||||
|
fullName: String
|
||||||
|
role: UserBaseRole
|
||||||
|
}
|
||||||
|
|
||||||
|
class Staff {
|
||||||
|
id: UUID
|
||||||
|
userId: String
|
||||||
|
fullName: String
|
||||||
|
ownerId: UUID
|
||||||
|
hubId: UUID
|
||||||
|
rollId: UUID
|
||||||
|
status: BackgroundCheckStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
class Vendor {
|
||||||
|
id: UUID
|
||||||
|
userId: String
|
||||||
|
companyName: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class Business {
|
||||||
|
id: UUID
|
||||||
|
userId: String
|
||||||
|
businessName: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class TeamMember {
|
||||||
|
id: UUID
|
||||||
|
teamId: UUID
|
||||||
|
userId: String
|
||||||
|
role: TeamMemberRole
|
||||||
|
}
|
||||||
|
|
||||||
|
class ActivityLog {
|
||||||
|
id: UUID
|
||||||
|
userId: String
|
||||||
|
activityType: ActivityType
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserConversation {
|
||||||
|
conversationId: UUID
|
||||||
|
userId: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class Conversation {
|
||||||
|
id: UUID
|
||||||
|
conversationType: ConversationType
|
||||||
|
}
|
||||||
|
|
||||||
|
class Message{
|
||||||
|
id: UUID
|
||||||
|
conversationId: UUID
|
||||||
|
content: String
|
||||||
|
}
|
||||||
|
|
||||||
|
User <|-- Staff
|
||||||
|
User <|-- Business
|
||||||
|
User <|-- Vendor
|
||||||
|
User <|-- TeamMember
|
||||||
|
|
||||||
|
|
||||||
|
User "1" -- "*" ActivityLog : logs
|
||||||
|
User "1" -- "*" UserConversation : participates in
|
||||||
|
UserConversation "*" -- "1" Conversation : is part of
|
||||||
|
Conversation "1" -- "*" Message : has
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
---
|
||||||
|
config:
|
||||||
|
layout: dagre
|
||||||
|
---
|
||||||
|
classDiagram
|
||||||
|
direction TB
|
||||||
|
class User {
|
||||||
|
id: String
|
||||||
|
email: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class Vendor {
|
||||||
|
id: UUID
|
||||||
|
userId: String
|
||||||
|
companyName: String
|
||||||
|
tier: VendorTier
|
||||||
|
}
|
||||||
|
|
||||||
|
class Staff {
|
||||||
|
id: UUID
|
||||||
|
userId: String
|
||||||
|
fullName: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class VendorBenefitPlan {
|
||||||
|
id: UUID
|
||||||
|
vendorId: UUID
|
||||||
|
title: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class InvoiceTemplate {
|
||||||
|
id: UUID
|
||||||
|
name: String
|
||||||
|
ownerId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
class VendorRate {
|
||||||
|
id: UUID
|
||||||
|
vendorId: UUID
|
||||||
|
roleName: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class AttireOption{
|
||||||
|
id: UUID
|
||||||
|
vendorId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
class Team {
|
||||||
|
id: UUID
|
||||||
|
teamName: String
|
||||||
|
ownerId: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class TeamMember {
|
||||||
|
id: UUID
|
||||||
|
teamId: UUID
|
||||||
|
userId: String
|
||||||
|
role: TeamMemberRole
|
||||||
|
}
|
||||||
|
|
||||||
|
class TeamHub {
|
||||||
|
id: UUID
|
||||||
|
teamId: UUID
|
||||||
|
hubName: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class Business {
|
||||||
|
id: UUID
|
||||||
|
userId: String
|
||||||
|
businessName: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClientFeedback {
|
||||||
|
id: UUID
|
||||||
|
businessId: UUID
|
||||||
|
vendorId: UUID
|
||||||
|
rating: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
class Order {
|
||||||
|
id: UUID
|
||||||
|
vendorId: UUID
|
||||||
|
businessId: UUID
|
||||||
|
status: OrderStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
class Shift {
|
||||||
|
id: UUID
|
||||||
|
orderId: UUID
|
||||||
|
status: ShiftStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
class Role {
|
||||||
|
id: UUID
|
||||||
|
name: String
|
||||||
|
vendorId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShiftRole {
|
||||||
|
shiftId: UUID
|
||||||
|
roleId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
class Workforce {
|
||||||
|
id: UUID
|
||||||
|
vendorId: UUID
|
||||||
|
staffId: UUID
|
||||||
|
status: WorkforceStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
class Application {
|
||||||
|
id: UUID
|
||||||
|
shiftId: UUID
|
||||||
|
staffId: UUID
|
||||||
|
roleId: UUID
|
||||||
|
status: ApplicationStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
class Assignment {
|
||||||
|
id: UUID
|
||||||
|
workforceId: UUID
|
||||||
|
shiftId: UUID
|
||||||
|
roleId: UUID
|
||||||
|
status: AssignmentStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
class Invoice {
|
||||||
|
id: UUID
|
||||||
|
vendorId: UUID
|
||||||
|
businessId: UUID
|
||||||
|
orderId: UUID
|
||||||
|
status: InvoiceStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
class RecentPayment {
|
||||||
|
id: UUID
|
||||||
|
staffId: UUID
|
||||||
|
applicationId: UUID
|
||||||
|
invoiceId: UUID
|
||||||
|
status: RecentPaymentStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
note for Vendor "All tables has relationship with vendor"
|
||||||
|
|
||||||
|
User "1" -- "1" Vendor : has
|
||||||
|
Vendor "1" o-- "1" Staff : (ownerId)
|
||||||
|
Vendor "1" -- "*" VendorBenefitPlan : offers
|
||||||
|
Vendor "1" -- "*" AttireOption
|
||||||
|
Vendor o-- InvoiceTemplate : (ownerId)
|
||||||
|
Vendor "1" -- "*" VendorRate : has
|
||||||
|
Vendor "1" -- "*" ClientFeedback : receives
|
||||||
|
Vendor "1" -- "*" Order : receives
|
||||||
|
Business "1" -- "*" Order : places
|
||||||
|
Order "1" -- "*" Shift : contains
|
||||||
|
Shift "1" -- "*" ShiftRole : requires
|
||||||
|
Role "1" -- "*" ShiftRole : fills
|
||||||
|
ShiftRole "1" -- "*" Application : receives
|
||||||
|
Assignment "1" -- "1" ShiftRole
|
||||||
|
Assignment "1" -- "1" Workforce
|
||||||
|
Invoice "1" -- "1" Order
|
||||||
|
Invoice "1" -- "*" RecentPayment : details
|
||||||
|
Vendor "1" o-- "1" Team
|
||||||
|
Team "1" -- "*" TeamHub : contains
|
||||||
|
Team "1" -- "*" TeamMember : has
|
||||||
@@ -1,358 +0,0 @@
|
|||||||
# Centralized Error Handling Architecture
|
|
||||||
**Project:** KROW Workforce Mobile App
|
|
||||||
|
|
||||||
## 1. Executive Summary
|
|
||||||
We have implemented a **Centralized Error Handling System** that ensures the entire application (Staff & Client) handles errors consistently, reliably, and with full localization support.
|
|
||||||
|
|
||||||
Instead of writing error handling code in every single feature (which leads to bugs and inconsistent messages), we rely on a global safety net that catches **Network Failures**, **Server Errors (500)**, **Not Found Errors (404)**, and **Business Logic Violations** automatically.
|
|
||||||
|
|
||||||
### Key Benefits
|
|
||||||
* **Safety:** The app never crashes due to unhandled API errors.
|
|
||||||
* **Consistency:** A network error looks the same in "Shifts" as it does in "billing".
|
|
||||||
* **Localization:** All error messages are automatically translated (English/Spanish).
|
|
||||||
* **Speed:** Developers can build features faster without worrying about `try/catch` blocks.
|
|
||||||
|
|
||||||
### Technical Excellence (Status Code Handling)
|
|
||||||
We don't just catch "errors"; we understand them. The system automatically categorizes and handles:
|
|
||||||
* **500 (Server Error):** "Our servers are having a moment. Please try again."
|
|
||||||
* **404 (Not Found):** "The resource you're looking for (Shift/Profile) is missing."
|
|
||||||
* **401 (Unauthorized):** "Your session expired. Please log in again." (Auto-redirect)
|
|
||||||
* **403 (Forbidden):** "You don't have permission to access this area."
|
|
||||||
* **503 (Unavailable):** "Maintenance mode or overloaded. Back in a bit!"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Architecture Overview
|
|
||||||
|
|
||||||
The error handling flows through three distinct layers, ensuring separation of concerns:
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
graph TD
|
|
||||||
A[Data Layer / Repository] -->|Throws AppException| B[BLoC Layer / State Management]
|
|
||||||
B -->|Emits Error Key| C[UI Layer / Presentation]
|
|
||||||
|
|
||||||
subgraph "1. Data Layer (The Guard)"
|
|
||||||
A -- Captures Exceptions --> D[DataErrorHandler Mixin]
|
|
||||||
D -- Maps to --> E[NetworkException, ServerException, etc.]
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph "2. BLoC Layer (The Translator)"
|
|
||||||
B -- Uses --> F[BlocErrorHandler Mixin]
|
|
||||||
F -- Catches AppException --> G[converts to 'errors.category.type']
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph "3. UI Layer (The Messenger)"
|
|
||||||
C -- Calls --> H["translateErrorKey()"]
|
|
||||||
H -- Returns --> I["Localized String (e.g. 'Sin conexión')"]
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
### 1. The Data Layer (The Guard)
|
|
||||||
**Location:** `packages/data_connect/lib/src/mixins/data_error_handler.dart`
|
|
||||||
|
|
||||||
This is where raw exceptions (from Firebase, network, etc.) are caught and converted into **typed exceptions** that the rest of the app can understand.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
```dart
|
|
||||||
mixin DataErrorHandler {
|
|
||||||
Future<T> handleDataOperation<T>(Future<T> Function() operation) async {
|
|
||||||
try {
|
|
||||||
return await operation();
|
|
||||||
} on SocketException {
|
|
||||||
throw NetworkException('errors.network.no_connection');
|
|
||||||
} on HttpException catch (e) {
|
|
||||||
if (e.statusCode == 500) throw ServerException('errors.server.internal');
|
|
||||||
if (e.statusCode == 404) throw NotFoundException('errors.not_found.resource');
|
|
||||||
// ... more mappings
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. The BLoC Layer (The Translator)
|
|
||||||
**Location:** `packages/core/lib/src/presentation/mixins/bloc_error_handler.dart`
|
|
||||||
|
|
||||||
BLoCs use this mixin to catch exceptions and convert them into **error keys** that can be localized.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
```dart
|
|
||||||
mixin BlocErrorHandler {
|
|
||||||
String handleError(Object error) {
|
|
||||||
if (error is NetworkException) return error.message;
|
|
||||||
if (error is ServerException) return error.message;
|
|
||||||
return 'errors.unknown';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. The UI Layer (The Messenger)
|
|
||||||
**Location:** `packages/core_localization/lib/src/utils/error_translator.dart`
|
|
||||||
|
|
||||||
The UI calls `translateErrorKey()` to convert error keys into user-friendly, localized messages.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
```dart
|
|
||||||
String translateErrorKey(String key) {
|
|
||||||
final t = LocaleSettings.instance.currentTranslations;
|
|
||||||
return t[key] ?? 'An error occurred';
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Real-World Example: Submitting a Tax Form
|
|
||||||
|
|
||||||
Let's trace what happens when a user submits Form W-4 with no internet:
|
|
||||||
|
|
||||||
1. **User Action:** Clicks "Submit Form"
|
|
||||||
2. **BLoC:** `FormW4Bloc` calls `submitW4UseCase.call(formData)`
|
|
||||||
3. **Use Case:** Calls `taxFormsRepository.submitW4(formData)`
|
|
||||||
4. **Repository (Data Layer):**
|
|
||||||
```dart
|
|
||||||
Future<void> submitW4(W4Data data) async {
|
|
||||||
return handleDataOperation(() async {
|
|
||||||
await _api.submitW4(data); // This throws SocketException
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
5. **DataErrorHandler Mixin:** Catches `SocketException` → throws `NetworkException('errors.network.no_connection')`
|
|
||||||
6. **BLoC:** Catches `NetworkException` → emits `FormW4State.error(errorMessage: 'errors.network.no_connection')`
|
|
||||||
7. **UI (FormW4Page):**
|
|
||||||
```dart
|
|
||||||
BlocListener<FormW4Bloc, FormW4State>(
|
|
||||||
listener: (context, state) {
|
|
||||||
if (state.status == FormW4Status.error) {
|
|
||||||
UiSnackbar.show(
|
|
||||||
context,
|
|
||||||
message: translateErrorKey(state.errorMessage!),
|
|
||||||
type: UiSnackbarType.error,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
```
|
|
||||||
8. **translateErrorKey:** Looks up `'errors.network.no_connection'` in `en.i18n.json` → returns `"No internet connection. Please check your network."`
|
|
||||||
9. **User Sees:** A friendly snackbar with the localized message
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Simple Verification Tests (For Non-Developers)
|
|
||||||
|
|
||||||
### Test A: The "Tunnel" Test (Network)
|
|
||||||
1. Open the app to the **Shifts** page.
|
|
||||||
2. Toggle **Airplane Mode ON**.
|
|
||||||
3. Pull to refresh the list.
|
|
||||||
4. **Result:** App shows a gentle `snackbar` error: *"No internet connection"* (or Spanish equivalent). **No Crash.**
|
|
||||||
|
|
||||||
### Test B: The "Duplicate Data" Test (Smart Validation)
|
|
||||||
1. Log in on two devices with the same account (if possible) or simply use a known registered email.
|
|
||||||
2. Go to the **Sign Up** page.
|
|
||||||
3. Try to register a new account using that *existing* email.
|
|
||||||
4. **Result:** App instantly displays specific, helpful feedback: *"An account with this email already exists."* instead of a generic failure.
|
|
||||||
5. **Why it matters:** Proves the backend and frontend are synced to guide the user, not just block them.
|
|
||||||
|
|
||||||
### Test C: The "Crash Proof" Test (The Safety Net)
|
|
||||||
1. **Scenario:** Even if a developer introduces a bug (like a random exception) or the server returns a 500 status.
|
|
||||||
2. **Result:** The app catches the unknown error, logs it internally, and shows a safe default message: *"Something went wrong. Please try again."*
|
|
||||||
3. **Why it matters:** The app never crashes or closes unexpectedly, preserving user trust.
|
|
||||||
|
|
||||||
### Test D: The "Language" Test (Localization)
|
|
||||||
1. Trigger an error (like wrong password).
|
|
||||||
2. Change phone language to **Spanish**.
|
|
||||||
3. Trigger the same error.
|
|
||||||
4. **Result:** Message automatically translates: *"Correo electrónico o contraseña inválidos."*
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Comprehensive Testing Guide for Client Verification
|
|
||||||
|
|
||||||
This section provides a complete testing checklist to verify that centralized error handling is working correctly across the entire Staff app.
|
|
||||||
|
|
||||||
### 🎯 **Testing Objectives**
|
|
||||||
- Verify all errors are caught and handled gracefully (no crashes)
|
|
||||||
- Confirm error messages are user-friendly and localized
|
|
||||||
- Ensure consistent error display using `UiSnackbar`
|
|
||||||
- Validate that error keys are properly translated
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 📱 **Test Suite A: Tax Forms (I-9 & W-4)**
|
|
||||||
|
|
||||||
These forms have been fully integrated with centralized error handling and localization.
|
|
||||||
|
|
||||||
#### **Test A1: Form Validation Errors**
|
|
||||||
1. Navigate to **Profile → Documents → Form W-4**
|
|
||||||
2. Try to proceed to next step **without filling required fields**
|
|
||||||
3. **Expected Result:**
|
|
||||||
- Validation error appears in a snackbar
|
|
||||||
- Message is clear and specific (e.g., "First name is required")
|
|
||||||
- Error is localized (Spanish: "Se requiere el nombre")
|
|
||||||
|
|
||||||
#### **Test A2: Network Error During Submission**
|
|
||||||
1. Fill out **Form I-9** completely
|
|
||||||
2. **Turn off WiFi/Mobile Data**
|
|
||||||
3. Click **Submit Form**
|
|
||||||
4. **Expected Result:**
|
|
||||||
- Snackbar shows: "No internet connection. Please check your network."
|
|
||||||
- Spanish: "Sin conexión a internet. Verifica tu red."
|
|
||||||
- Form data is NOT lost
|
|
||||||
|
|
||||||
#### **Test A3: Server Error Simulation**
|
|
||||||
1. Fill out **Form W-4**
|
|
||||||
2. If backend is accessible, trigger a 500 error
|
|
||||||
3. **Expected Result:**
|
|
||||||
- Snackbar shows: "Our servers are having issues. Please try again."
|
|
||||||
- Spanish: "Nuestros servidores tienen problemas. Inténtalo de nuevo."
|
|
||||||
|
|
||||||
#### **Test A4: Language Switching**
|
|
||||||
1. Navigate to **Form I-9**
|
|
||||||
2. Trigger any error (validation or network)
|
|
||||||
3. Note the error message
|
|
||||||
4. **Change device language to Spanish** (Settings → Language)
|
|
||||||
5. Trigger the same error
|
|
||||||
6. **Expected Result:**
|
|
||||||
- Error message appears in Spanish
|
|
||||||
- All form labels and hints are also in Spanish
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 📱 **Test Suite B: Shifts & Availability**
|
|
||||||
|
|
||||||
#### **Test B1: Network Error on Shifts Page**
|
|
||||||
1. Navigate to **Shifts** tab
|
|
||||||
2. **Enable Airplane Mode**
|
|
||||||
3. Pull to refresh
|
|
||||||
4. **Expected Result:**
|
|
||||||
- Snackbar: "No internet connection"
|
|
||||||
- No crash or blank screen
|
|
||||||
- Previous data (if any) remains visible
|
|
||||||
|
|
||||||
#### **Test B2: Shift Not Found (404)**
|
|
||||||
1. If possible, try to access a deleted/non-existent shift
|
|
||||||
2. **Expected Result:**
|
|
||||||
- Snackbar: "Shift not found"
|
|
||||||
- User is redirected back to shifts list
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 📱 **Test Suite C: Profile & Authentication**
|
|
||||||
|
|
||||||
#### **Test C1: Session Expiry (401)**
|
|
||||||
1. Let the app sit idle for extended period (or manually invalidate token)
|
|
||||||
2. Try to perform any action (update profile, submit form)
|
|
||||||
3. **Expected Result:**
|
|
||||||
- Snackbar: "Your session has expired. Please log in again."
|
|
||||||
- App automatically redirects to login screen
|
|
||||||
|
|
||||||
#### **Test C2: Profile Update Errors**
|
|
||||||
1. Navigate to **Profile → Personal Info**
|
|
||||||
2. Try to update with invalid data (e.g., invalid email format)
|
|
||||||
3. **Expected Result:**
|
|
||||||
- Validation error in snackbar
|
|
||||||
- Specific message about what's wrong
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 📱 **Test Suite D: Payments & Bank Account**
|
|
||||||
|
|
||||||
#### **Test D1: Bank Account Addition Error**
|
|
||||||
1. Navigate to **Profile → Bank Account**
|
|
||||||
2. Try to add account with invalid routing number
|
|
||||||
3. **Expected Result:**
|
|
||||||
- Snackbar shows validation error
|
|
||||||
- Error is localized
|
|
||||||
|
|
||||||
#### **Test D2: Payment History Network Error**
|
|
||||||
1. Navigate to **Payments** tab
|
|
||||||
2. **Turn off internet**
|
|
||||||
3. Try to load payment history
|
|
||||||
4. **Expected Result:**
|
|
||||||
- Snackbar: "No internet connection"
|
|
||||||
- No crash
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 📱 **Test Suite E: Clock In & Attendance**
|
|
||||||
|
|
||||||
#### **Test E1: Clock In Network Error**
|
|
||||||
1. Navigate to **Clock In** tab
|
|
||||||
2. **Disable network**
|
|
||||||
3. Try to clock in
|
|
||||||
4. **Expected Result:**
|
|
||||||
- Snackbar: "No internet connection"
|
|
||||||
- Clock in action is blocked until network returns
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🌐 **Test Suite F: Localization Verification**
|
|
||||||
|
|
||||||
#### **Test F1: English Error Messages**
|
|
||||||
With device language set to **English**, verify these error keys translate correctly:
|
|
||||||
|
|
||||||
| Scenario | Expected English Message |
|
|
||||||
|----------|-------------------------|
|
|
||||||
| No internet | "No internet connection. Please check your network." |
|
|
||||||
| Server error (500) | "Our servers are having issues. Please try again." |
|
|
||||||
| Not found (404) | "The requested resource was not found." |
|
|
||||||
| Unauthorized (401) | "Your session has expired. Please log in again." |
|
|
||||||
| Validation error | Specific field error (e.g., "Email is required") |
|
|
||||||
|
|
||||||
#### **Test F2: Spanish Error Messages**
|
|
||||||
With device language set to **Español**, verify these error keys translate correctly:
|
|
||||||
|
|
||||||
| Scenario | Expected Spanish Message |
|
|
||||||
|----------|-------------------------|
|
|
||||||
| No internet | "Sin conexión a internet. Verifica tu red." |
|
|
||||||
| Server error (500) | "Nuestros servidores tienen problemas. Inténtalo de nuevo." |
|
|
||||||
| Not found (404) | "No se encontró el recurso solicitado." |
|
|
||||||
| Unauthorized (401) | "Tu sesión ha expirado. Inicia sesión nuevamente." |
|
|
||||||
| Validation error | Error específico del campo |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### ✅ **Success Criteria**
|
|
||||||
|
|
||||||
The centralized error handling is working correctly if:
|
|
||||||
|
|
||||||
1. **No Crashes**: App never crashes due to network/server errors
|
|
||||||
2. **Consistent Display**: All errors appear in `UiSnackbar` with same styling
|
|
||||||
3. **User-Friendly**: Messages are clear, specific, and actionable
|
|
||||||
4. **Localized**: All errors translate correctly to Spanish
|
|
||||||
5. **Graceful Degradation**: App remains usable even when errors occur
|
|
||||||
6. **Data Preservation**: Form data is not lost when errors happen
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🐛 **What to Report if Tests Fail**
|
|
||||||
|
|
||||||
If any test fails, please report:
|
|
||||||
1. **Which test** (e.g., "Test A2: Network Error During Submission")
|
|
||||||
2. **What happened** (e.g., "App crashed" or "Error showed in English despite Spanish language")
|
|
||||||
3. **Screenshot** of the error (if visible)
|
|
||||||
4. **Steps to reproduce**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🔧 **Quick Debug Commands**
|
|
||||||
|
|
||||||
For developers debugging error handling:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
// Test error translation directly
|
|
||||||
print(translateErrorKey('errors.network.no_connection'));
|
|
||||||
print(translateErrorKey('errors.server.internal'));
|
|
||||||
print(translateErrorKey('errors.not_found.shift'));
|
|
||||||
|
|
||||||
// Test in Spanish
|
|
||||||
LocaleSettings.setLocale(AppLocale.es);
|
|
||||||
print(translateErrorKey('errors.network.no_connection'));
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Code Locations (Reference)
|
|
||||||
* **Exceptions:** `packages/domain/lib/src/exceptions/app_exception.dart`
|
|
||||||
* **Data Mixin:** `packages/data_connect/lib/src/mixins/data_error_handler.dart`
|
|
||||||
* **Bloc Mixin:** `packages/core/lib/src/presentation/mixins/bloc_error_handler.dart`
|
|
||||||
* **Translator:** `packages/core_localization/lib/src/utils/error_translator.dart`
|
|
||||||
* **Strings:** `packages/core_localization/lib/src/l10n/*.i18n.json`
|
|
||||||
@@ -1,908 +0,0 @@
|
|||||||
# 🧪 KROW Workforce Platform - QA Testing Checklist
|
|
||||||
|
|
||||||
**Version:** 1.0
|
|
||||||
**Date:** February 1, 2026
|
|
||||||
**Coverage:** Client App + Staff App
|
|
||||||
**Purpose:** Manual QA and Regression Testing
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 TABLE OF CONTENTS
|
|
||||||
|
|
||||||
1. [Feature-Level QA Checklist](#1️⃣-feature-level-qa-checklist)
|
|
||||||
- [Client App Features](#client-app-features)
|
|
||||||
- [Staff App Features](#staff-app-features)
|
|
||||||
2. [Cross-Application Test Scenarios](#2️⃣-cross-application-test-scenarios)
|
|
||||||
3. [Shared Infrastructure Validation](#3️⃣-shared-infrastructure-validation)
|
|
||||||
4. [Regression & Release Checklist](#4️⃣-regression--release-checklist)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1️⃣ FEATURE-LEVEL QA CHECKLIST
|
|
||||||
|
|
||||||
### CLIENT APP FEATURES
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 CLIENT-001: Authentication
|
|
||||||
|
|
||||||
**Applications:** Client
|
|
||||||
**Entry Points:**
|
|
||||||
- Launch app → Get Started → Sign In
|
|
||||||
- Launch app → Get Started → Sign Up
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Sign in with valid email and password displays home dashboard
|
|
||||||
- [ ] Sign up with business details creates account and navigates to home
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] Invalid email format shows validation error
|
|
||||||
- [ ] Incorrect password shows authentication error
|
|
||||||
- [ ] Weak password in sign-up shows strength requirements
|
|
||||||
- [ ] Duplicate email in sign-up shows "already registered" error
|
|
||||||
- [ ] Empty fields show required field errors
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Loading spinner displays during authentication
|
|
||||||
- [ ] OAuth redirect shows appropriate loading state
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 CLIENT-002: Home Dashboard
|
|
||||||
|
|
||||||
**Applications:** Client
|
|
||||||
**Entry Points:**
|
|
||||||
- Home tab (bottom navigation)
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Dashboard displays current day coverage widget
|
|
||||||
- [ ] Spending analytics widget shows correct totals
|
|
||||||
- [ ] Recent reorders display completed shift roles
|
|
||||||
- [ ] Quick action buttons navigate to correct features
|
|
||||||
- [ ] Drag-and-drop widget reordering works correctly
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] Empty state shows "No data available" when no orders exist
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Empty coverage shows "No shifts today"
|
|
||||||
- [ ] Empty reorders shows "No recent orders"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 CLIENT-003: Create Order
|
|
||||||
|
|
||||||
**Applications:** Client
|
|
||||||
**Entry Points:**
|
|
||||||
- Home → Create Order button
|
|
||||||
- Orders tab → + FAB button
|
|
||||||
- Order type → One-Time
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Order type selection displays.
|
|
||||||
- [ ] Hub selection shows list of business hubs
|
|
||||||
- [ ] Role selection displays vendor roles
|
|
||||||
- [ ] Position quantity can be incremented/decremented (min 1)
|
|
||||||
- [ ] Date picker displays correct calendar
|
|
||||||
- [ ] Time pickers show valid time ranges
|
|
||||||
- [ ] Break duration affects total hours calculation
|
|
||||||
- [ ] Cost preview calculates correctly (rate × positions × hours)
|
|
||||||
- [ ] Order submission creates order, shift, and shift roles
|
|
||||||
- [ ] Success confirmation displays after submission
|
|
||||||
- [ ] New order appears in View Orders list
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] Empty hub field shows validation error
|
|
||||||
- [ ] Empty role field shows validation error
|
|
||||||
- [ ] Zero positions shows validation error
|
|
||||||
- [ ] Invalid date (past) shows validation error
|
|
||||||
- [ ] Start time after end time shows validation error
|
|
||||||
- [ ] Missing required fields prevent submission
|
|
||||||
- [ ] Backend validation errors display appropriately
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Hub list shows "No hubs" if none exist
|
|
||||||
- [ ] Role list shows "No roles" if none configured
|
|
||||||
- [ ] Loading spinner displays during submission
|
|
||||||
- [ ] Submission progress indicator updates
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 CLIENT-004: View Orders
|
|
||||||
|
|
||||||
**Applications:** Client
|
|
||||||
**Entry Points:**
|
|
||||||
- Orders tab (bottom navigation)
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Orders list displays orders for selected date
|
|
||||||
- [ ] Calendar date selection updates order list
|
|
||||||
- [ ] Each order card shows hub name and address
|
|
||||||
- [ ] Each order card shows shift time range
|
|
||||||
- [ ] Each order card shows role positions (filled/total)
|
|
||||||
- [ ] Each order card shows hourly rate and total cost
|
|
||||||
- [ ] Accepted applications section displays confirmed staff
|
|
||||||
- [ ] Staff names and photos display correctly
|
|
||||||
- [ ] Order list scrolls smoothly with many orders
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] Invalid date selection shows error
|
|
||||||
|
|
||||||
- [ ] Missing staff data shows placeholder
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Empty date shows "No orders for this date"
|
|
||||||
- [ ] Empty accepted applications shows "No confirmed staff"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 CLIENT-005: Coverage Monitoring
|
|
||||||
|
|
||||||
**Applications:** Client
|
|
||||||
**Entry Points:**
|
|
||||||
- Coverage tab (bottom navigation)
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Coverage overview displays current date
|
|
||||||
- [ ] Coverage stats show needed/confirmed/checked-in counts
|
|
||||||
- [ ] Shift cards display hub name and time range
|
|
||||||
- [ ] Worker cards show staff name and photo
|
|
||||||
- [ ] Check-in status indicators update correctly (late, en-route, checked-in)
|
|
||||||
- [ ] Late workers display with warning indicator
|
|
||||||
- [ ] Coverage progress bar updates correctly
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] Missing worker photo shows default avatar
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
|
|
||||||
- [ ] Empty coverage shows "No shifts today"
|
|
||||||
- [ ] No workers show "No staff assigned"
|
|
||||||
|
|
||||||
**State Persistence:**
|
|
||||||
- [ ] Coverage data refreshes automatically every X minutes
|
|
||||||
- [ ] Manual refresh via pull-to-refresh gesture
|
|
||||||
|
|
||||||
**Backend Dependency Validation:**
|
|
||||||
- [ ] `listShiftRolesByBusinessAndDateRange` returns shift requirements
|
|
||||||
- [ ] `listStaffsApplicationsByBusinessForDay` returns staff status
|
|
||||||
- [ ] Attendance status correctly mapped from backend
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 CLIENT-006: Billing & Invoices
|
|
||||||
|
|
||||||
**Applications:** Client
|
|
||||||
**Entry Points:**
|
|
||||||
- Billing tab (bottom navigation)
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Current bill amount displays correctly
|
|
||||||
- [ ] Pending invoices list shows open invoices
|
|
||||||
- [ ] Invoice history shows paid invoices
|
|
||||||
- [ ] Savings amount displays correctly
|
|
||||||
- [ ] Spending breakdown shows costs by role
|
|
||||||
- [ ] Period filter (weekly/monthly) updates data
|
|
||||||
- [ ] Invoice detail view shows line items
|
|
||||||
- [ ] Invoice PDF download works (if implemented)
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] Zero billing shows $0.00 (not error)
|
|
||||||
- [ ] Negative savings shows correctly
|
|
||||||
- [ ] Missing invoice data shows placeholder
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Empty pending invoices shows "No pending invoices"
|
|
||||||
- [ ] Empty history shows "No invoice history"
|
|
||||||
- [ ] Empty spending breakdown shows "No spending data"
|
|
||||||
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 CLIENT-007: Hub Management
|
|
||||||
|
|
||||||
**Applications:** Client
|
|
||||||
**Entry Points:**
|
|
||||||
- Settings → Hubs
|
|
||||||
- Create Order → Add Hub button
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Hubs list displays all business hubs
|
|
||||||
- [ ] Hub cards show name and full address
|
|
||||||
- [ ] Add hub button opens creation form
|
|
||||||
- [ ] Google Places autocomplete suggests addresses
|
|
||||||
- [ ] Address selection auto-fills all address fields
|
|
||||||
- [ ] Hub name can be customized
|
|
||||||
- [ ] Hub creation adds to list immediately
|
|
||||||
- [ ] Hub deletion removes from list (with confirmation)
|
|
||||||
- [ ] Team entity auto-created for business if missing
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] Empty hub name shows validation error
|
|
||||||
- [ ] Empty address shows validation error
|
|
||||||
- [ ] Invalid address format shows error
|
|
||||||
- [ ] Duplicate hub name shows warning
|
|
||||||
- [ ] Hub with active orders prevents deletion (validation error)
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Empty hubs list shows "No hubs configured"
|
|
||||||
- [ ] Hub creation shows loading spinner
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 CLIENT-008: Settings
|
|
||||||
|
|
||||||
**Applications:** Client
|
|
||||||
**Entry Points:**
|
|
||||||
- Settings (navigation menu)
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] User profile displays name and email
|
|
||||||
- [ ] Business name displays correctly
|
|
||||||
- [ ] Hubs link navigates to hub management
|
|
||||||
- [ ] Sign out logs out user and returns to auth screen
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] Missing profile photo shows default avatar
|
|
||||||
- [ ] Sign out error shows retry option
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Profile data loads on page mount
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 CLIENT-009: Client Main Navigation
|
|
||||||
|
|
||||||
**Applications:** Client
|
|
||||||
**Entry Points:**
|
|
||||||
- Main app shell after authentication
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Bottom navigation displays 5 tabs (Home, Coverage, Billing, Orders, Reports)
|
|
||||||
- [ ] Tab selection updates active indicator
|
|
||||||
- [ ] Tab selection navigates to correct feature
|
|
||||||
- [ ] Deep links navigate to correct tab
|
|
||||||
- [ ] Back button navigates correctly within nested routes
|
|
||||||
- [ ] Tab state persists after device rotation
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] Invalid route shows 404 or redirects to home
|
|
||||||
- [ ] Reports tab shows placeholder (not yet implemented)
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Navigation bar displays immediately
|
|
||||||
- [ ] Initial tab loads first
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### STAFF APP FEATURES
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 STAFF-001: Authentication
|
|
||||||
|
|
||||||
**Applications:** Staff
|
|
||||||
**Entry Points:**
|
|
||||||
- Launch app → Get Started → Phone Verification
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Phone number entry accepts valid formats
|
|
||||||
- [ ] OTP sent confirmation displays
|
|
||||||
- [ ] OTP verification succeeds with valid code
|
|
||||||
- [ ] Profile setup wizard displays for new users
|
|
||||||
- [ ] Authenticated users bypass auth and show home
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] Invalid phone format shows validation error
|
|
||||||
- [ ] Incorrect OTP shows verification error
|
|
||||||
- [ ] Expired OTP shows re-send option
|
|
||||||
- [ ] Empty fields show required field errors
|
|
||||||
- [ ] Network error displays retry option
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Loading spinner displays during phone verification
|
|
||||||
- [ ] OTP input shows countdown timer
|
|
||||||
- [ ] Profile setup shows progress indicator
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 STAFF-002: Home Dashboard
|
|
||||||
|
|
||||||
**Applications:** Staff
|
|
||||||
**Entry Points:**
|
|
||||||
- Home tab (bottom navigation)
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Today's shifts display with time and location
|
|
||||||
- [ ] Tomorrow's shifts display correctly
|
|
||||||
- [ ] Recommended shifts show available opportunities
|
|
||||||
- [ ] Shift cards show role, location, and pay rate
|
|
||||||
- [ ] Quick actions navigate to correct features
|
|
||||||
- [ ] Dashboard refreshes on pull-to-refresh
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] Missing shift data shows placeholder
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Empty today's shifts shows "No shifts today"
|
|
||||||
- [ ] Empty recommended shows "No available shifts"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 STAFF-003: Profile
|
|
||||||
|
|
||||||
**Applications:** Staff
|
|
||||||
**Entry Points:**
|
|
||||||
- Profile tab (bottom navigation)
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Profile displays name, email, phone, and photo
|
|
||||||
- [ ] Statistics show total shifts, ratings, reliability score
|
|
||||||
- [ ] Profile sections list displays all sections
|
|
||||||
- [ ] Section navigation works correctly
|
|
||||||
- [ ] Sign out logs out user and returns to auth screen
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] Missing profile photo shows default avatar
|
|
||||||
- [ ] Missing statistics show 0 or default values
|
|
||||||
- [ ] Sign out error shows retry option
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Profile data loads on page mount
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 STAFF-004: Shifts Management
|
|
||||||
|
|
||||||
**Applications:** Staff
|
|
||||||
**Entry Points:**
|
|
||||||
- Shifts tab (bottom navigation)
|
|
||||||
- Tab navigation: My Shifts / Available / Pending / Cancelled / History
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] My Shifts tab displays assigned shifts
|
|
||||||
- [ ] Available Shifts tab shows open positions
|
|
||||||
- [ ] Pending tab shows applications awaiting approval
|
|
||||||
- [ ] Cancelled tab shows cancelled shifts
|
|
||||||
- [ ] History tab shows past shifts
|
|
||||||
- [ ] Shift detail view displays full information
|
|
||||||
- [ ] Accept shift updates status to confirmed
|
|
||||||
- [ ] Decline shift updates status to declined
|
|
||||||
- [ ] Apply for shift creates application
|
|
||||||
- [ ] Shift cards show time, location, role, and pay
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] Empty tabs show appropriate empty state messages
|
|
||||||
- [ ] Already applied shift prevents duplicate application
|
|
||||||
- [ ] Past shifts cannot be applied to
|
|
||||||
- [ ] Cancelled shifts show cancellation reason
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 STAFF-005: Availability Management
|
|
||||||
|
|
||||||
**Applications:** Staff
|
|
||||||
**Entry Points:**
|
|
||||||
- Worker Main → Availability
|
|
||||||
- Profile → Availability section
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Weekly grid displays Monday-Sunday
|
|
||||||
- [ ] Time slots (Morning/Afternoon/Evening) toggle correctly
|
|
||||||
- [ ] Quick-set buttons work (Weekdays/Weekends/All Week)
|
|
||||||
- [ ] Individual day/slot updates save correctly
|
|
||||||
- [ ] Green checkmarks indicate availability
|
|
||||||
- [ ] Gray states indicate unavailability
|
|
||||||
- [ ] Changes save automatically
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
|
|
||||||
- [ ] Save failure shows error message
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Loading spinner displays while fetching availability
|
|
||||||
- [ ] Default state shows all unavailable
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 STAFF-006: Clock In/Out
|
|
||||||
|
|
||||||
**Applications:** Staff
|
|
||||||
**Entry Points:**
|
|
||||||
- Clock In tab (bottom navigation)
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Today's shift displays with clock in button
|
|
||||||
- [ ] Clock in button creates attendance record
|
|
||||||
- [ ] Clock in time displays correctly
|
|
||||||
- [ ] Clock out button appears after clocking in
|
|
||||||
- [ ] Clock out creates end time record
|
|
||||||
- [ ] Total hours calculated correctly
|
|
||||||
- [ ] Attendance status updates immediately
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] No shift today shows "No shifts to clock in"
|
|
||||||
- [ ] Already clocked in prevents duplicate clock in
|
|
||||||
|
|
||||||
- [ ] Clock in outside shift time shows warning
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Loading spinner displays while fetching shift
|
|
||||||
- [ ] Empty state shows "No shifts scheduled"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 STAFF-007: Payments
|
|
||||||
|
|
||||||
**Applications:** Staff
|
|
||||||
**Entry Points:**
|
|
||||||
- Payments tab (bottom navigation)
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Payment summary displays total earnings
|
|
||||||
- [ ] Payment history lists all transactions
|
|
||||||
- [ ] Payment cards show amount, date, and status
|
|
||||||
- [ ] Payment detail view shows breakdown
|
|
||||||
- [ ] Filter by date range works correctly
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] Zero earnings show $0.00 (not error)
|
|
||||||
- [ ] Missing payment data shows placeholder
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Empty history shows "No payment history"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 STAFF-008: Personal Info (Onboarding)
|
|
||||||
|
|
||||||
**Applications:** Staff
|
|
||||||
**Entry Points:**
|
|
||||||
- Profile → Personal Info
|
|
||||||
- Onboarding wizard
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Form displays current profile data
|
|
||||||
- [ ] Name field allows text input
|
|
||||||
- [ ] Email field validates email format
|
|
||||||
- [ ] Phone field validates phone format
|
|
||||||
- [ ] Photo upload works correctly
|
|
||||||
- [ ] Preferred locations multi-select works
|
|
||||||
- [ ] Save button updates profile
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] Empty required fields show validation errors
|
|
||||||
- [ ] Invalid email format shows error
|
|
||||||
- [ ] Invalid phone format shows error
|
|
||||||
|
|
||||||
- [ ] Photo upload failure shows error
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Form loads with skeleton placeholders
|
|
||||||
- [ ] Photo upload shows progress indicator
|
|
||||||
- [ ] Save button shows loading spinner
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 STAFF-009: Emergency Contact (Onboarding)
|
|
||||||
|
|
||||||
**Applications:** Staff
|
|
||||||
**Entry Points:**
|
|
||||||
- Profile → Emergency Contact
|
|
||||||
- Onboarding wizard
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Contact list displays all contacts
|
|
||||||
- [ ] Add contact button opens form
|
|
||||||
- [ ] Contact form validates name and phone
|
|
||||||
- [ ] Relationship dropdown shows options (Family/Spouse/Friend/Other)
|
|
||||||
- [ ] Remove contact deletes from list
|
|
||||||
- [ ] Save updates all contacts
|
|
||||||
- [ ] Multiple contacts supported
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] Empty name shows validation error
|
|
||||||
- [ ] Invalid phone format shows error
|
|
||||||
- [ ] At least one contact required (if applicable)
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Loading spinner displays while fetching contacts
|
|
||||||
- [ ] Empty state shows "No emergency contacts"
|
|
||||||
- [ ] Save button shows loading spinner
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 STAFF-010: Experience & Skills (Onboarding)
|
|
||||||
|
|
||||||
**Applications:** Staff
|
|
||||||
**Entry Points:**
|
|
||||||
- Profile → Experience
|
|
||||||
- Onboarding wizard
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Industries multi-select displays options
|
|
||||||
- [ ] Skills multi-select displays options
|
|
||||||
- [ ] Selected items show checkmarks
|
|
||||||
- [ ] Deselection removes items
|
|
||||||
- [ ] Save updates profile
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] At least one industry required (if applicable)
|
|
||||||
- [ ] At least one skill required (if applicable)
|
|
||||||
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Loading spinner displays while fetching data
|
|
||||||
- [ ] Save button shows loading spinner
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 STAFF-012: Bank Account (Finances)
|
|
||||||
|
|
||||||
**Applications:** Staff
|
|
||||||
**Entry Points:**
|
|
||||||
- Profile → Bank Account
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Account list displays all accounts
|
|
||||||
- [ ] Add account button opens form
|
|
||||||
- [ ] Form validates routing and account numbers
|
|
||||||
- [ ] Account type dropdown shows options (Checking/Savings)
|
|
||||||
- [ ] First account auto-sets as primary
|
|
||||||
- [ ] Save adds account to list
|
|
||||||
- [ ] Primary account indicator displays
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] Empty routing number shows validation error
|
|
||||||
- [ ] Invalid routing number format shows error
|
|
||||||
- [ ] Empty account number shows validation error
|
|
||||||
- [ ] Invalid account number format shows error
|
|
||||||
- [ ] Duplicate account shows warning
|
|
||||||
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Loading spinner displays while fetching accounts
|
|
||||||
- [ ] Empty state shows "No bank accounts"
|
|
||||||
- [ ] Save button shows loading spinner
|
|
||||||
|
|
||||||
**State Persistence:**
|
|
||||||
- [ ] Accounts persist after save
|
|
||||||
- [ ] Account list refreshes after addition
|
|
||||||
|
|
||||||
**Backend Dependency Validation:**
|
|
||||||
- [ ] `getAccountsByOwnerId` fetches staff accounts
|
|
||||||
- [ ] `createAccount` creates new account
|
|
||||||
- [ ] First account auto-flagged as primary
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 STAFF-013: Time Card History (Finances)
|
|
||||||
|
|
||||||
**Applications:** Staff
|
|
||||||
**Entry Points:**
|
|
||||||
- Profile → Time Card
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Time card list displays all records
|
|
||||||
- [ ] Each card shows shift details (date, time, location)
|
|
||||||
- [ ] Each card shows clock in/out times
|
|
||||||
- [ ] Each card shows total hours worked
|
|
||||||
- [ ] Scrolling loads more records (pagination)
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] Missing attendance data shows "Not recorded"
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Empty state shows "No time card history"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 STAFF-014: Tax Forms (Compliance)
|
|
||||||
|
|
||||||
**Applications:** Staff
|
|
||||||
**Entry Points:**
|
|
||||||
- Profile → Tax Forms
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Forms list displays required forms (I-9, W-4)
|
|
||||||
- [ ] Form status shows completed/incomplete
|
|
||||||
- [ ] I-9 form opens editor
|
|
||||||
- [ ] I-9 form validates all fields
|
|
||||||
- [ ] W-4 form opens editor
|
|
||||||
- [ ] W-4 form validates all fields
|
|
||||||
- [ ] Form submission updates status to completed
|
|
||||||
- [ ] Completed forms show edit option
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] Empty required fields show validation errors
|
|
||||||
- [ ] Invalid SSN format shows error
|
|
||||||
- [ ] Invalid date format shows error
|
|
||||||
- [ ] Signature required validation
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Loading spinner displays while fetching forms
|
|
||||||
- [ ] Form editor loads with skeleton placeholders
|
|
||||||
- [ ] Save button shows loading spinner
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 📱 STAFF-017: Staff Main Navigation
|
|
||||||
|
|
||||||
**Applications:** Staff
|
|
||||||
**Entry Points:**
|
|
||||||
- Main app shell after authentication
|
|
||||||
|
|
||||||
**Happy Path Test Cases:**
|
|
||||||
- [ ] Bottom navigation displays 5 tabs (Shifts, Payments, Home, Clock In, Profile)
|
|
||||||
- [ ] Tab selection updates active indicator
|
|
||||||
- [ ] Tab selection navigates to correct feature
|
|
||||||
- [ ] Deep links navigate to correct tab and nested route
|
|
||||||
- [ ] Back button navigates correctly within nested routes
|
|
||||||
- [ ] Tab state persists after device rotation
|
|
||||||
- [ ] Nested routes (onboarding, emergency-contact, etc.) accessible
|
|
||||||
|
|
||||||
**Validation & Error States:**
|
|
||||||
- [ ] Invalid route shows 404 or redirects to home
|
|
||||||
- [ ] Navigation errors log appropriately
|
|
||||||
|
|
||||||
**Loading & Empty States:**
|
|
||||||
- [ ] Navigation bar displays immediately
|
|
||||||
- [ ] Initial tab loads first
|
|
||||||
|
|
||||||
**State Persistence:**
|
|
||||||
- [ ] Active tab persists after app background → foreground
|
|
||||||
- [ ] Tab state resets to home on app restart
|
|
||||||
|
|
||||||
**Backend Dependency Validation:**
|
|
||||||
- [ ] No direct backend calls (navigation only)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2️⃣ CROSS-APPLICATION TEST SCENARIOS
|
|
||||||
|
|
||||||
### Scenario 1: Order Creation → Staff Application Flow
|
|
||||||
|
|
||||||
**Preconditions:**
|
|
||||||
- Client user authenticated
|
|
||||||
- Staff user authenticated
|
|
||||||
- At least one hub configured
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
1. **CLIENT APP:**
|
|
||||||
- [ ] Create one-time order with specific hub, role, date, and time
|
|
||||||
- [ ] Verify order appears in View Orders list
|
|
||||||
- [ ] Verify shift shows as unfilled (0/X positions)
|
|
||||||
|
|
||||||
2. **STAFF APP:**
|
|
||||||
- [ ] Open Shifts tab → Available tab
|
|
||||||
- [ ] Verify new shift appears in available list
|
|
||||||
- [ ] Verify shift details match order (hub, role, time, pay)
|
|
||||||
- [ ] Apply for shift position
|
|
||||||
|
|
||||||
3. **CLIENT APP:**
|
|
||||||
- [ ] Refresh View Orders
|
|
||||||
- [ ] Verify shift shows pending application (0/X filled, pending)
|
|
||||||
|
|
||||||
4. **STAFF APP:**
|
|
||||||
- [ ] Verify application appears in Pending tab
|
|
||||||
- [ ] Verify shift removed from Available tab
|
|
||||||
|
|
||||||
**Expected Results:**
|
|
||||||
- ✅ Order created in Client appears in Staff Available Shifts
|
|
||||||
- ✅ Application in Staff shows pending in both apps
|
|
||||||
- ✅ Shift counts update correctly in real-time
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Scenario 2: Shift Acceptance → Coverage Tracking
|
|
||||||
|
|
||||||
**Preconditions:**
|
|
||||||
- Scenario 1 completed (pending application exists)
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
1. **STAFF APP:**
|
|
||||||
- [ ] Go to Shifts → Pending tab
|
|
||||||
- [ ] Accept pending shift assignment
|
|
||||||
|
|
||||||
2. **CLIENT APP:**
|
|
||||||
- [ ] Refresh View Orders
|
|
||||||
- [ ] Verify shift shows as filled (1/X positions)
|
|
||||||
- [ ] Verify staff name and photo appear in accepted applications
|
|
||||||
- [ ] Navigate to Coverage tab
|
|
||||||
- [ ] Verify shift appears with assigned staff
|
|
||||||
|
|
||||||
3. **STAFF APP:**
|
|
||||||
- [ ] Verify shift moved from Pending to My Shifts tab
|
|
||||||
- [ ] Verify shift appears on Home dashboard
|
|
||||||
|
|
||||||
**Expected Results:**
|
|
||||||
- ✅ Accepted shift reflects in Client orders immediately
|
|
||||||
- ✅ Staff appears in Coverage monitoring
|
|
||||||
- ✅ Shift moves to My Shifts in Staff app
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Scenario 3: Clock In → Real-Time Coverage Update
|
|
||||||
|
|
||||||
**Preconditions:**
|
|
||||||
- Scenario 2 completed (staff has accepted shift)
|
|
||||||
- Current date/time is during shift window
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
1. **STAFF APP:**
|
|
||||||
- [ ] Navigate to Clock In tab
|
|
||||||
- [ ] Verify today's shift displays
|
|
||||||
- [ ] Click Clock In button
|
|
||||||
- [ ] Verify clock in time recorded
|
|
||||||
|
|
||||||
2. **CLIENT APP:**
|
|
||||||
- [ ] Navigate to Coverage tab
|
|
||||||
- [ ] Verify staff status changed to "Checked In"
|
|
||||||
- [ ] Verify check-in time displays
|
|
||||||
- [ ] Verify coverage stats updated (checked-in count incremented)
|
|
||||||
|
|
||||||
3. **STAFF APP:**
|
|
||||||
- [ ] Wait until shift end time
|
|
||||||
- [ ] Click Clock Out button
|
|
||||||
- [ ] Verify clock out time recorded
|
|
||||||
|
|
||||||
4. **CLIENT APP:**
|
|
||||||
- [ ] Refresh Coverage tab
|
|
||||||
- [ ] Verify staff status changed to "Completed"
|
|
||||||
|
|
||||||
5. **STAFF APP:**
|
|
||||||
- [ ] Navigate to Time Card
|
|
||||||
- [ ] Verify attendance record appears with correct times and hours
|
|
||||||
|
|
||||||
**Expected Results:**
|
|
||||||
- ✅ Clock in updates Coverage status in Client
|
|
||||||
- ✅ Clock out completes attendance record
|
|
||||||
- ✅ Time card displays correct hours in Staff app
|
|
||||||
- ✅ Coverage monitoring reflects real-time status
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Scenario 4: Hub Creation → Order Placement
|
|
||||||
|
|
||||||
**Preconditions:**
|
|
||||||
- Client user authenticated
|
|
||||||
- No existing hubs
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
1. **CLIENT APP:**
|
|
||||||
- [ ] Navigate to Settings → Hubs
|
|
||||||
- [ ] Verify empty state "No hubs configured"
|
|
||||||
- [ ] Click Add Hub button
|
|
||||||
- [ ] Enter hub name and use Google Places autocomplete
|
|
||||||
- [ ] Select address from suggestions
|
|
||||||
- [ ] Verify address fields auto-filled
|
|
||||||
- [ ] Save hub
|
|
||||||
|
|
||||||
2. **CLIENT APP:**
|
|
||||||
- [ ] Navigate to Create Order
|
|
||||||
- [ ] Verify new hub appears in hub selection list
|
|
||||||
- [ ] Select new hub and complete order creation
|
|
||||||
|
|
||||||
3. **STAFF APP:**
|
|
||||||
- [ ] Navigate to Shifts → Available
|
|
||||||
- [ ] Verify shift shows correct hub name and address
|
|
||||||
|
|
||||||
**Expected Results:**
|
|
||||||
- ✅ Hub created in Settings appears in order creation
|
|
||||||
- ✅ Hub address propagates to shift details in Staff app
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Scenario 5: Shift Cancellation → Staff Notification
|
|
||||||
|
|
||||||
**Preconditions:**
|
|
||||||
- Staff has accepted shift assignment
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
1. **CLIENT APP:**
|
|
||||||
- [ ] Navigate to View Orders
|
|
||||||
- [ ] Select order with assigned staff
|
|
||||||
- [ ] Cancel shift (if feature exists) or delete order
|
|
||||||
|
|
||||||
2. **STAFF APP:**
|
|
||||||
- [ ] Refresh Shifts tab
|
|
||||||
- [ ] Verify shift moved to Cancelled tab
|
|
||||||
- [ ] Verify shift removed from My Shifts
|
|
||||||
- [ ] Verify cancellation reason displays
|
|
||||||
|
|
||||||
3. **STAFF APP:**
|
|
||||||
- [ ] Verify shift removed from Home dashboard
|
|
||||||
|
|
||||||
**Expected Results:**
|
|
||||||
- ✅ Cancelled shift moves to Cancelled tab
|
|
||||||
- ✅ Shift removed from active assignments
|
|
||||||
- ⚠️ **Requires clarification:** Cancellation feature may not be fully implemented
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Scenario 6: Authentication State Sharing
|
|
||||||
|
|
||||||
**Preconditions:**
|
|
||||||
- Neither app authenticated
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
1. **CLIENT APP:**
|
|
||||||
- [ ] Sign in with email/password
|
|
||||||
- [ ] Verify Firebase Auth token generated
|
|
||||||
|
|
||||||
2. **STAFF APP:**
|
|
||||||
- [ ] Launch app
|
|
||||||
- [ ] Verify Staff app requires separate authentication
|
|
||||||
- [ ] Verify Client session does not carry over
|
|
||||||
|
|
||||||
3. **CLIENT APP:**
|
|
||||||
- [ ] Sign out
|
|
||||||
|
|
||||||
4. **STAFF APP:**
|
|
||||||
- [ ] Verify Staff app session persists (independent)
|
|
||||||
|
|
||||||
**Expected Results:**
|
|
||||||
- ✅ Client and Staff apps maintain independent auth sessions
|
|
||||||
- ✅ Signing out of one app does not affect the other
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Scenario 7: Data Created in Client → Visible in Staff
|
|
||||||
|
|
||||||
**Preconditions:**
|
|
||||||
- Client creates multiple orders
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
1. **CLIENT APP:**
|
|
||||||
- [ ] Create 5 orders on different dates
|
|
||||||
- [ ] Create 3 orders on same date with different hubs
|
|
||||||
|
|
||||||
2. **STAFF APP:**
|
|
||||||
- [ ] Navigate to Shifts → Available
|
|
||||||
- [ ] Verify all 8 shifts appear
|
|
||||||
- [ ] Verify date grouping correct
|
|
||||||
- [ ] Verify hub addresses correct
|
|
||||||
- [ ] Apply for 2 shifts
|
|
||||||
|
|
||||||
3. **CLIENT APP:**
|
|
||||||
- [ ] Navigate to View Orders
|
|
||||||
- [ ] Verify 2 shifts show pending applications
|
|
||||||
- [ ] Navigate to Coverage
|
|
||||||
- [ ] Verify 0 checked-in (pending acceptance)
|
|
||||||
|
|
||||||
**Expected Results:**
|
|
||||||
- ✅ All orders visible in both apps
|
|
||||||
- ✅ Application states sync correctly
|
|
||||||
- ✅ Data consistency maintained across apps
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Scenario 8: Role-Based Access Differences
|
|
||||||
|
|
||||||
**Preconditions:**
|
|
||||||
- Client user authenticated
|
|
||||||
- Staff user authenticated
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
1. **CLIENT APP:**
|
|
||||||
- [ ] Navigate to Billing
|
|
||||||
- [ ] Verify billing data displays (Client-only feature)
|
|
||||||
- [ ] Navigate to Create Order
|
|
||||||
- [ ] Verify order creation available (Client-only feature)
|
|
||||||
|
|
||||||
2. **STAFF APP:**
|
|
||||||
- [ ] Verify no Billing tab exists
|
|
||||||
- [ ] Verify no Create Order feature
|
|
||||||
- [ ] Navigate to Availability
|
|
||||||
- [ ] Verify availability editing available (Staff-only feature)
|
|
||||||
|
|
||||||
3. **CLIENT APP:**
|
|
||||||
- [ ] Verify no Availability feature exists
|
|
||||||
- [ ] Verify no Clock In feature exists
|
|
||||||
|
|
||||||
**Expected Results:**
|
|
||||||
- ✅ Client app has business management features (orders, billing, hubs)
|
|
||||||
- ✅ Staff app has worker features (availability, clock in, payments)
|
|
||||||
- ✅ No feature overlap or unauthorized access
|
|
||||||
|
|
||||||
---
|
|
||||||
@@ -60,43 +60,43 @@
|
|||||||
"icon": "bi-geo-alt"
|
"icon": "bi-geo-alt"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/diagrams/dataconnect/uml/user_uml_diagram.mmd",
|
"path": "docs/DATACONNECT_GUIDES/DIAGRAMS/uml/user_uml_diagram.mmd",
|
||||||
"title": "User UML Diagram Dataconnect",
|
"title": "User UML Diagram Dataconnect",
|
||||||
"type": "mermaid",
|
"type": "mermaid",
|
||||||
"icon": "bi-diagram-2"
|
"icon": "bi-diagram-2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/diagrams/dataconnect/uml/staff_uml_diagram.mmd",
|
"path": "docs/DATACONNECT_GUIDES/DIAGRAMS/uml/staff_uml_diagram.mmd",
|
||||||
"title": "Staff UML Diagram Dataconnect",
|
"title": "Staff UML Diagram Dataconnect",
|
||||||
"type": "mermaid",
|
"type": "mermaid",
|
||||||
"icon": "bi-diagram-2"
|
"icon": "bi-diagram-2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/diagrams/dataconnect/uml/business_uml_diagram.mmd",
|
"path": "docs/DATACONNECT_GUIDES/DIAGRAMS/uml/business_uml_diagram.mmd",
|
||||||
"title": "Business UML Diagram Dataconnect",
|
"title": "Business UML Diagram Dataconnect",
|
||||||
"type": "mermaid",
|
"type": "mermaid",
|
||||||
"icon": "bi-diagram-3"
|
"icon": "bi-diagram-3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/diagrams/dataconnect/uml/vendor_uml_diagram_simplify.mmd",
|
"path": "docs/DATACONNECT_GUIDES/DIAGRAMS/uml/vendor_uml_diagram_simplify.mmd",
|
||||||
"title": "Vendor UML Diagram Dataconnect",
|
"title": "Vendor UML Diagram Dataconnect",
|
||||||
"type": "mermaid",
|
"type": "mermaid",
|
||||||
"icon": "bi-diagram-3"
|
"icon": "bi-diagram-3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/diagrams/dataconnect/uml/team_uml_diagram.mmd",
|
"path": "docs/DATACONNECT_GUIDES/DIAGRAMS/uml/team_uml_diagram.mmd",
|
||||||
"title": "Team UML Diagram Dataconnect",
|
"title": "Team UML Diagram Dataconnect",
|
||||||
"type": "mermaid",
|
"type": "mermaid",
|
||||||
"icon": "bi-diagram-2"
|
"icon": "bi-diagram-2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/diagrams/dataconnect/mobile/client_app_diagram.mmd",
|
"path": "docs/DATACONNECT_GUIDES/DIAGRAMS/mobile/client_app_diagram.mmd",
|
||||||
"title": "Client App Diagram Dataconnect",
|
"title": "Client App Diagram Dataconnect",
|
||||||
"type": "mermaid",
|
"type": "mermaid",
|
||||||
"icon": "bi-diagram-2"
|
"icon": "bi-diagram-2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "assets/diagrams/dataconnect/mobile/staff_app_diagram.mmd",
|
"path": "docs/DATACONNECT_GUIDES/DIAGRAMS/mobile/staff_app_diagram.mmd",
|
||||||
"title": "Staff App Diagram Dataconnect",
|
"title": "Staff App Diagram Dataconnect",
|
||||||
"type": "mermaid",
|
"type": "mermaid",
|
||||||
"icon": "bi-diagram-2"
|
"icon": "bi-diagram-2"
|
||||||
|
|||||||
@@ -65,10 +65,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Dataconnect guide",
|
"title": "Dataconnect guide",
|
||||||
"path": "./assets/documents/dataconnect/backend_manual.md"
|
"path": "docs/DATACONNECT_GUIDES/DOCUMENTS/backend_manual.md"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Schema Dataconnect guide",
|
"title": "Schema Dataconnect guide",
|
||||||
"path": "./assets/documents/dataconnect/schema_dataconnect_guide.md"
|
"path": "docs/DATACONNECT_GUIDES/DOCUMENTS/schema_dataconnect_guide.md"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user