|
|
|
|
@@ -52,4 +52,175 @@
|
|
|
|
|
- BenefitsOverviewPage also has CircularProgressIndicator (not shimmer-ified yet)
|
|
|
|
|
- ShiftDetailsPage has a dialog-level spinner in the "applying" dialog -- this is intentional, not a page loading state
|
|
|
|
|
- Hub details/edit pages use CircularProgressIndicator as action overlays (save/delete) -- keep as-is, not initial load
|
|
|
|
|
- Client home page has no loading spinner; it renders with default empty dashboard data
|
|
|
|
|
- Client home page uses shimmer skeleton during loading (ClientHomePageSkeleton + ClientHomeHeaderSkeleton)
|
|
|
|
|
|
|
|
|
|
## V2 API Migration Patterns
|
|
|
|
|
- `BaseApiService` is registered in `CoreModule` as a lazy singleton (injected as `i.get<BaseApiService>()`)
|
|
|
|
|
- `BaseApiService` type lives in `krow_domain`; `ApiService` impl lives in `krow_core`
|
|
|
|
|
- V2 endpoints: `V2ApiEndpoints.staffDashboard` etc. from `krow_core/core.dart`
|
|
|
|
|
- V2 domain shift entities: `TodayShift`, `AssignedShift`, `OpenShift` (separate from core `Shift`)
|
|
|
|
|
- V2 `Benefit`: uses `targetHours`/`trackedHours`/`remainingHours` (int) -- old used `entitlementHours`/`usedHours` (double)
|
|
|
|
|
- Staff dashboard endpoint returns all home data in one call (todaysShifts, tomorrowsShifts, recommendedShifts, benefits, staffName)
|
|
|
|
|
- Navigator has `toShiftDetailsById(String shiftId)` for cases where only the ID is available
|
|
|
|
|
- `StaffDashboard` entity updated to use typed lists: `List<TodayShift>`, `List<AssignedShift>`, `List<OpenShift>`
|
|
|
|
|
- Staff home feature migrated (Phase 2): removed krow_data_connect, firebase_data_connect, staff_shifts deps
|
|
|
|
|
- [V2 Profile Migration](project_v2_profile_migration.md) -- entity mappings and DI patterns for all profile sub-packages
|
|
|
|
|
- Staff clock-in migrated (Phase 3): repo impl → V2 API, removed Data Connect deps
|
|
|
|
|
- V2 `Shift` entity: `startsAt`/`endsAt` (DateTime), `locationName` (String?), no `startTime`/`endTime`/`clientName`/`hourlyRate`/`location`
|
|
|
|
|
- V2 `AttendanceStatus`: `isClockedIn` getter (not `isCheckedIn`), `clockInAt` (not `checkInTime`), no `checkOutTime`/`activeApplicationId`
|
|
|
|
|
- `AttendanceStatus` constructor requires `attendanceStatus: AttendanceStatusType.notClockedIn` for default
|
|
|
|
|
- Clock-out uses `shiftId` (not `applicationId`) -- V2 API resolves assignment from shiftId
|
|
|
|
|
- `listTodayShifts` endpoint returns `{ items: [...] }` with TodayShift-like shape (no lat/lng, hourlyRate, clientName)
|
|
|
|
|
- `getCurrentAttendanceStatus` returns flat object `{ activeShiftId, attendanceStatus, clockInAt }`
|
|
|
|
|
- Clock-in/out POST endpoints return `{ attendanceEventId, assignmentId, sessionId, status, validationStatus }` -- repo re-fetches status after
|
|
|
|
|
- Geofence: lat/lng not available from listTodayShifts or shiftDetail endpoints (lives on clock_points table, not returned by BE)
|
|
|
|
|
- `BaseApiService` not exported from `krow_core/core.dart` -- must import from `krow_domain/krow_domain.dart`
|
|
|
|
|
|
|
|
|
|
## Staff Shifts Feature Migration (Phase 3 -- completed)
|
|
|
|
|
- Migrated from `krow_data_connect` + `DataConnectService` to `BaseApiService` + `V2ApiEndpoints`
|
|
|
|
|
- Removed deps: `krow_data_connect`, `firebase_auth`, `firebase_data_connect`, `geolocator`, `google_maps_flutter`, `meta`
|
|
|
|
|
- State uses 5 typed lists: `List<AssignedShift>`, `List<OpenShift>`, `List<PendingAssignment>`, `List<CancelledShift>`, `List<CompletedShift>`
|
|
|
|
|
- ShiftDetail (not Shift) used for detail page -- loaded by BLoC via API, not passed as route argument
|
|
|
|
|
- Money: `hourlyRateCents` (int) / 100 for display -- all V2 shift entities use cents
|
|
|
|
|
- Dates: All V2 entities have `DateTime` fields (not `String`) -- no more `DateTime.parse()` in widgets
|
|
|
|
|
- AssignmentStatus enum drives bottom bar logic (accepted=clock-in, assigned=accept/decline, null=apply)
|
|
|
|
|
- Old `Shift` entity still exists in domain but only used by clock-in feature -- shift list/detail pages use V2 entities
|
|
|
|
|
- ShiftDetailsModule route no longer receives `Shift` data argument -- uses `shiftId` param only
|
|
|
|
|
- `toShiftDetailsById(String)` is the standard navigation for V2 (no entity passing)
|
|
|
|
|
- Profile completion: moved into feature repo impl via `V2ApiEndpoints.staffProfileCompletion` + `ProfileCompletion.fromJson`
|
|
|
|
|
- Find Shifts tab: removed geolocator distance filter and multi-day grouping (V2 API handles server-side)
|
|
|
|
|
- Renamed use cases: `GetMyShiftsUseCase` → `GetAssignedShiftsUseCase`, `GetAvailableShiftsUseCase` → `GetOpenShiftsUseCase`, `GetHistoryShiftsUseCase` → `GetCompletedShiftsUseCase`, `GetShiftDetailsUseCase` → `GetShiftDetailUseCase`
|
|
|
|
|
|
|
|
|
|
## Client Home Feature Migration (Phase 4 -- completed)
|
|
|
|
|
- Migrated from `krow_data_connect` + `DataConnectService` to `BaseApiService` + `V2ApiEndpoints`
|
|
|
|
|
- Removed deps: `krow_data_connect`, `firebase_data_connect`, `intl`
|
|
|
|
|
- V2 entities: `ClientDashboard` (replaces `HomeDashboardData`), `RecentOrder` (replaces `ReorderItem`)
|
|
|
|
|
- `ClientDashboard` contains nested `SpendingSummary`, `CoverageMetrics`, `LiveActivityMetrics`
|
|
|
|
|
- Money: `weeklySpendCents`, `projectedNext7DaysCents`, `averageShiftCostCents` (int) / 100 for display
|
|
|
|
|
- Two API calls: `GET /client/dashboard` (all metrics + user/biz info) and `GET /client/reorders` (returns `{ items: [...] }`)
|
|
|
|
|
- Removed `GetUserSessionDataUseCase` -- user/business info now part of `ClientDashboard`
|
|
|
|
|
- `LiveActivityWidget` rewritten from StatefulWidget with direct DC calls to StatelessWidget consuming BLoC state
|
|
|
|
|
- Dead code removed: `ShiftOrderFormSheet`, `ClientHomeSheets`, `CoverageDashboard` (all unused)
|
|
|
|
|
- `RecentOrder` entity: `id`, `title`, `date` (DateTime?), `hubName` (String?), `positionCount` (int), `orderType` (OrderType)
|
|
|
|
|
- Module imports `CoreModule()` (not `DataConnectModule()`), injects `BaseApiService` into repo
|
|
|
|
|
- State has `dashboard` (ClientDashboard?) with computed getters `businessName`, `userName`
|
|
|
|
|
- No photoUrl in V2 dashboard response -- header shows letter avatar only
|
|
|
|
|
|
|
|
|
|
## Client Billing Feature Migration (Phase 4 -- completed)
|
|
|
|
|
- Migrated from `krow_data_connect` + `BillingConnectorRepository` to `BaseApiService` + `V2ApiEndpoints`
|
|
|
|
|
- Removed deps: `krow_data_connect`, `firebase_data_connect`
|
|
|
|
|
- Deleted old presentation models: `BillingInvoice`, `BillingWorkerRecord`, `SpendingBreakdownItem`
|
|
|
|
|
- V2 domain entities used directly: `Invoice`, `BillingAccount`, `SpendItem`, `CurrentBill`, `Savings`
|
|
|
|
|
- Old domain types removed: `BusinessBankAccount`, `InvoiceItem`, `InvoiceWorker`, `BillingPeriod` enum
|
|
|
|
|
- Money: all amounts in cents (int). State has computed `currentBillDollars`, `savingsDollars`, `spendTotalCents` getters
|
|
|
|
|
- `Invoice` V2 entity: `invoiceId`, `invoiceNumber`, `amountCents` (int), `status` (InvoiceStatus enum), `dueDate`, `paymentDate`, `vendorId`, `vendorName`
|
|
|
|
|
- `BillingAccount` V2 entity: `accountId`, `bankName`, `providerReference`, `last4`, `isPrimary`, `accountType` (AccountType enum)
|
|
|
|
|
- `SpendItem` V2 entity: `category`, `amountCents` (int), `percentage` (double) -- server-side aggregation by role
|
|
|
|
|
- Spend breakdown: replaced `BillingPeriod` enum with `BillingPeriodTab` (local) + `SpendBreakdownParams` (startDate/endDate ISO strings)
|
|
|
|
|
- API response shapes: list endpoints return `{ items: [...] }`, scalar endpoints spread data (`{ currentBillCents, requestId }`)
|
|
|
|
|
- Approve/dispute: POST to `V2ApiEndpoints.clientInvoiceApprove(id)` / `clientInvoiceDispute(id)`
|
|
|
|
|
- Completion review page: `BillingInvoice` replaced with `Invoice` -- worker-level data not available in V2 (widget placeholder)
|
|
|
|
|
- `InvoiceStatus` enum has `.value` property for display and `fromJson` factory with safe fallback to `unknown`
|
|
|
|
|
|
|
|
|
|
## Client Reports Feature Migration (Phase 4 -- completed)
|
|
|
|
|
- Migrated from `krow_data_connect` + `ReportsConnectorRepository` to `BaseApiService` + `V2ApiEndpoints`
|
|
|
|
|
- Removed deps: `krow_data_connect`
|
|
|
|
|
- 7 report endpoints: summary, daily-ops, spend, coverage, forecast, performance, no-show
|
|
|
|
|
- Old `ReportsSummary` entity replaced with V2 `ReportSummary` (different fields: totalShifts, totalSpendCents, averageCoveragePercentage, averagePerformanceScore, noShowCount, forecastAccuracyPercentage)
|
|
|
|
|
- `businessId` removed from all events/repo -- V2 API resolves from auth token
|
|
|
|
|
- DailyOps: old `DailyOpsShift` replaced with `ShiftWithWorkers` (from coverage_domain). `TimeRange` has `startsAt`/`endsAt` (not `start`/`end`)
|
|
|
|
|
- Spend: `SpendReport` uses `totalSpendCents` (int), `chart` (List<SpendDataPoint> with `bucket`/`amountCents`), `breakdown` (List<SpendItem> with `category`/`amountCents`/`percentage`)
|
|
|
|
|
- Coverage: `CoverageReport` uses `averageCoveragePercentage`, `filledWorkers`, `neededWorkers`, `chart` (List<CoverageDayPoint> with `day`/`needed`/`filled`/`coveragePercentage`)
|
|
|
|
|
- Forecast: `ForecastReport` uses `forecastSpendCents`, `averageWeeklySpendCents`, `totalWorkerHours`, `weeks` (List<ForecastWeek> with `week`/`shiftCount`/`workerHours`/`forecastSpendCents`/`averageShiftCostCents`)
|
|
|
|
|
- Performance: V2 uses int percentages (`fillRatePercentage`, `completionRatePercentage`, `onTimeRatePercentage`) and `averageFillTimeMinutes` (double) -- convert to hours: `/60`
|
|
|
|
|
- NoShow: `NoShowReport` uses `totalNoShowCount`, `noShowRatePercentage`, `workersWhoNoShowed`, `items` (List<NoShowWorkerItem> with `staffId`/`staffName`/`incidentCount`/`riskStatus`/`incidents`)
|
|
|
|
|
- Module injects `BaseApiService` via `i.get<BaseApiService>()` -- no more `DataConnectModule` import
|
|
|
|
|
|
|
|
|
|
## Client Hubs Feature Migration (Phase 5 -- completed)
|
|
|
|
|
- Migrated from `krow_data_connect` + `HubsConnectorRepository` + `DataConnectService` to `BaseApiService` + `V2ApiEndpoints`
|
|
|
|
|
- Removed deps: `krow_data_connect`, `firebase_auth`, `firebase_data_connect`, `http`
|
|
|
|
|
- V2 `Hub` entity: `hubId` (not `id`), `fullAddress` (not `address`), `costCenterId`/`costCenterName` (flat, not nested `CostCenter` object)
|
|
|
|
|
- V2 `CostCenter` entity: `costCenterId` (not `id`), `name` only (no `code` field)
|
|
|
|
|
- V2 `HubManager` entity: `managerAssignmentId`, `businessMembershipId`, `managerId`, `name`
|
|
|
|
|
- API response shapes: `GET /client/hubs` returns `{ items: [...] }`, `GET /client/cost-centers` returns `{ items: [...] }`
|
|
|
|
|
- Create/update return `{ hubId, created: true }` / `{ hubId, updated: true }` -- repo returns hubId String
|
|
|
|
|
- Delete: soft-delete (sets status=INACTIVE). Backend rejects if hub has active orders (409 HUB_DELETE_BLOCKED)
|
|
|
|
|
- Assign NFC: `POST /client/hubs/:hubId/assign-nfc` with `{ nfcTagId }`
|
|
|
|
|
- Module no longer imports `DataConnectModule()` -- `BaseApiService` available from parent `CoreModule()`
|
|
|
|
|
- `UpdateHubArguments.id` renamed to `UpdateHubArguments.hubId`; `CreateHubArguments.address` renamed to `.fullAddress`
|
|
|
|
|
- `HubDetailsDeleteRequested.id` renamed to `.hubId`; `EditHubAddRequested.address` renamed to `.fullAddress`
|
|
|
|
|
- Navigator still passes full `Hub` entity via route args (not just hubId)
|
|
|
|
|
|
|
|
|
|
## Client Orders Feature Migration (Phase 5 -- completed)
|
|
|
|
|
- 3 sub-packages migrated: `orders_common`, `view_orders`, `create_order`
|
|
|
|
|
- Removed deps: `krow_data_connect`, `firebase_data_connect`, `firebase_auth` from all; kept `intl` in create_order and orders_common
|
|
|
|
|
- V2 `OrderItem` entity: `itemId`, `orderId`, `orderType` (OrderType enum), `roleName`, `date` (DateTime), `startsAt`/`endsAt` (DateTime), `requiredWorkerCount`, `filledCount`, `hourlyRateCents`, `totalCostCents` (int cents), `locationName` (String?), `status` (ShiftStatus enum), `workers` (List<AssignedWorkerSummary>)
|
|
|
|
|
- Old entities deleted: `OneTimeOrder`, `RecurringOrder`, `PermanentOrder`, `ReorderData`, `OneTimeOrderHubDetails`, `RecurringOrderHubDetails`
|
|
|
|
|
- `AssignedWorkerSummary`: `applicationId` (String?), `workerName` (String? -- nullable!), `role` (String?), `confirmationStatus` (ApplicationStatus?)
|
|
|
|
|
- V2 `Vendor` entity: field is `companyName` (not `name`) -- old code used `vendor.name`
|
|
|
|
|
- V2 `ShiftStatus` enum: only has `draft`, `open`, `pendingConfirmation`, `assigned`, `active`, `completed`, `cancelled`, `unknown` -- no `filled`/`confirmed`/`pending`
|
|
|
|
|
- `OrderType` enum has `unknown` variant -- must handle in switch statements
|
|
|
|
|
- View orders: removed `GetAcceptedApplicationsForDayUseCase` -- V2 returns workers inline with order items
|
|
|
|
|
- View orders cubit: date filtering now uses `_isSameDay(DateTime, DateTime)` instead of string comparison
|
|
|
|
|
- Create order BLoCs: build `Map<String, dynamic>` V2 payloads instead of old entity objects
|
|
|
|
|
- V2 create endpoints: `POST /client/orders/one-time` (requires `orderDate`), `/recurring` (requires `startDate`/`endDate`/`recurrenceDays`), `/permanent` (requires `startDate`/`daysOfWeek`)
|
|
|
|
|
- V2 edit endpoint: `POST /client/orders/:orderId/edit` -- creates edited copy, cancels original
|
|
|
|
|
- V2 cancel endpoint: `POST /client/orders/:orderId/cancel` with optional `reason`
|
|
|
|
|
- Reorder uses `OrderPreview` (from `V2ApiEndpoints.clientOrderReorderPreview`) instead of old `ReorderData`
|
|
|
|
|
- `OrderPreview` has nested `OrderPreviewShift` > `OrderPreviewRole` structure
|
|
|
|
|
- Query repo: `getHubs()` replaces `getHubsByOwner(businessId)` -- V2 resolves business from auth token
|
|
|
|
|
- `OneTimeOrderPosition` is now a typedef for `OrderPositionUiModel` from `orders_common`
|
|
|
|
|
- `OrderEditSheet` (1700 lines) fully rewritten: delegates to `IViewOrdersRepository` instead of direct DC calls
|
|
|
|
|
## Staff Authentication Feature Migration (Phase 6 -- completed)
|
|
|
|
|
- Migrated from `krow_data_connect` + `DataConnectService` + `firebase_data_connect` to `BaseApiService` + `V2ApiEndpoints`
|
|
|
|
|
- Removed deps: `krow_data_connect`, `firebase_data_connect`, `firebase_core`
|
|
|
|
|
- KEPT `firebase_auth` -- V2 backend `startStaffPhoneAuth` returns `CLIENT_FIREBASE_SDK` mode for mobile, meaning phone verification stays client-side via Firebase SDK
|
|
|
|
|
- Auth flow: Firebase SDK phone verify (client-side) -> get idToken -> `POST /auth/staff/phone/verify` with `{ idToken, mode }` -> V2 hydrates session (upserts user, loads actor context)
|
|
|
|
|
- V2 verify response: `{ sessionToken, refreshToken, expiresInSeconds, user: { id, email, displayName, phone }, staff: { staffId, tenantId, fullName, ... }, tenant, requiresProfileSetup }`
|
|
|
|
|
- `requiresProfileSetup` boolean replaces old signup logic (create user/staff via DC mutations)
|
|
|
|
|
- Profile setup: `POST /staff/profile/setup` with `{ fullName, bio, preferredLocations, maxDistanceMiles, industries, skills }`
|
|
|
|
|
- Sign out: `POST /auth/sign-out` (server-side token revocation) + local `FirebaseAuth.signOut()`
|
|
|
|
|
- `AuthInterceptor` in `DioClient` stays as-is -- attaches Firebase Bearer tokens to all V2 API requests
|
|
|
|
|
- `AuthInterceptor` in `DioClient` stays as-is -- attaches Firebase Bearer tokens to all V2 API requests
|
|
|
|
|
- Pre-existing issue: `ExperienceSkill` and `Industry` enums deleted from domain but still referenced in `profile_setup_experience.dart`
|
|
|
|
|
|
|
|
|
|
## Client Authentication Feature Migration (Phase 6 -- completed)
|
|
|
|
|
- Migrated from `krow_data_connect` + `DataConnectService` + `firebase_data_connect` to `BaseApiService` + `V2ApiEndpoints`
|
|
|
|
|
- Removed deps: `firebase_data_connect`, `firebase_core` from pubspec
|
|
|
|
|
- KEPT `firebase_auth` -- client-side sign-in needed so `AuthInterceptor` can attach Bearer tokens
|
|
|
|
|
- KEPT `krow_data_connect` -- only for `ClientSessionStore`/`ClientSession`/`ClientBusinessSession` (not yet extracted)
|
|
|
|
|
- Auth flow (Option A -- hybrid):
|
|
|
|
|
1. Firebase Auth client-side `signInWithEmailAndPassword` (sets `FirebaseAuth.instance.currentUser`)
|
|
|
|
|
2. `GET /auth/session` via V2 API (returns user + business + tenant context)
|
|
|
|
|
3. Populate `ClientSessionStore` from V2 session response
|
|
|
|
|
- Sign-up flow:
|
|
|
|
|
1. `POST /auth/client/sign-up` via V2 API (server-side: creates Firebase account + user/tenant/business/memberships in one transaction)
|
|
|
|
|
2. Local `signInWithEmailAndPassword` (sets local auth state)
|
|
|
|
|
3. `GET /auth/session` to load context + populate session store
|
|
|
|
|
- V2 session response shape: `{ user: { userId, email, displayName, phone, status }, business: { businessId, businessName, businessSlug, role, tenantId, membershipId }, tenant: {...}, vendor: null, staff: null }`
|
|
|
|
|
- Sign-out: `POST /auth/client/sign-out` (server-side revocation) + `FirebaseAuth.instance.signOut()` + `ClientSessionStore.instance.clear()`
|
|
|
|
|
- V2 sign-up error codes: `AUTH_PROVIDER_ERROR` with message containing `EMAIL_EXISTS` or `WEAK_PASSWORD`, `FORBIDDEN` for role mismatch
|
|
|
|
|
- Old Data Connect calls removed: `getUserById`, `getBusinessesByUserId`, `createBusiness`, `createUser`, `updateUser`, `deleteBusiness`
|
|
|
|
|
- Old rollback logic removed -- V2 API handles rollback server-side in one transaction
|
|
|
|
|
- Domain `User` entity: V2 uses `status: UserStatus` (not `role: String`) -- constructor: `User(id:, email:, displayName:, phone:, status:)`
|
|
|
|
|
- Module: `CoreModule()` (not `DataConnectModule()`), injects `BaseApiService` into `AuthRepositoryImpl`
|
|
|
|
|
|
|
|
|
|
## Client Settings Feature Migration (Phase 6 -- completed)
|
|
|
|
|
- Migrated sign-out from `DataConnectService.signOut()` to V2 API + local Firebase Auth
|
|
|
|
|
- Removed `DataConnectModule` import from module, replaced with `CoreModule()`
|
|
|
|
|
- `SettingsRepositoryImpl` now takes `BaseApiService` (not `DataConnectService`)
|
|
|
|
|
- Sign-out: `POST /auth/client/sign-out` + `FirebaseAuth.instance.signOut()` + `ClientSessionStore.instance.clear()`
|
|
|
|
|
- `settings_profile_header.dart` still reads from `ClientSessionStore` (now from `krow_core`)
|
|
|
|
|
|
|
|
|
|
## V2SessionService (Final Phase -- completed)
|
|
|
|
|
- `V2SessionService` singleton at `packages/core/lib/src/services/session/v2_session_service.dart`
|
|
|
|
|
- Replaces `DataConnectService` for session state management in both apps
|
|
|
|
|
- Uses `SessionHandlerMixin` from core (same interface as old DC version)
|
|
|
|
|
- `fetchUserRole()` calls `GET /auth/session` via `BaseApiService` (not DC connector)
|
|
|
|
|
- `signOut()` calls `POST /auth/sign-out` + `FirebaseAuth.signOut()` + `handleSignOut()`
|
|
|
|
|
- Registered in `CoreModule` via `i.addLazySingleton<V2SessionService>()` -- calls `setApiService()`
|
|
|
|
|
- Both `main.dart` files use `V2SessionService.instance.initializeAuthListener()` instead of `DataConnectService`
|
|
|
|
|
- Both `SessionListener` widgets subscribe to `V2SessionService.instance.onSessionStateChanged`
|
|
|
|
|
- `staff_main` package migrated: local repo/usecase via `V2ApiEndpoints.staffProfileCompletion` + `ProfileCompletion.fromJson`
|
|
|
|
|
- `krow_data_connect` removed from: staff app, client app, staff_main package pubspecs
|
|
|
|
|
- Session stores (`StaffSessionStore`, `ClientSessionStore`) now live in core, not data_connect
|
|
|
|
|
|