Stu Mason
Stu Mason

Activity

StuMason/cleanconnect
TidyLinker.com
TypeScript
Pull Request Merged

PR #145 merged: fix: cleaner onboarding completion safety net + admin diagnostics

Summary

Closes the gap that left Betty Jepkemoi (and any future cleaner in the same shape) stranded mid-onboarding — invisible in search forever despite having done all the work. Three layers of defence + admin tooling so the same class of bug can't silently bite us again.

Root cause (recap)

searchable() requires onboarding_completed_at IS NOT NULL. The only code path that wrote that column was OnboardingController::complete() — i.e. a single explicit "Finish" button click on Step 5. If a cleaner walked away pre-click (e.g. their docs were still pending admin approval), nothing else stamped them. Betty had services, rate, location, ID + RtW all approved, and was the only user in this state — but the bug class is structural and would catch others.

Fixes

1. MaybeCompleteOnboarding action (app/Actions/Cleaner/MaybeCompleteOnboarding.php) — checks every requirement (services, rate, location, verifications or company status) and stamps onboarding_completed_at if all met. Returns an OnboardingCompletionResult DTO listing any blockers so callers can show admins exactly what's missing. Idempotent.

2. Two auto-trigger points so cleaners self-heal:

  • Admin/VerificationController::approve calls it after a doc is approved (catches cleaners who walked away while waiting for admin review).
  • Cleaner/DashboardController::index calls it on every dashboard load (catches anything else, including legacy stranded users).

3. Save* actions are now advance-only on onboarding_step — replaces unconditional 'onboarding_step' => N writes with max(current, N). Editing earlier steps after reaching a later one no longer regresses the counter (Betty's onboarding_step was 2 despite being on Step 4+, exactly because of this).

Admin tooling (per the ask)

Admin → User detail (/admin/users/{user}):

  • New diagnostics panel renders when a cleaner's onboarding is incomplete.
  • Shows current step, lists missing requirements (e.g. "Base hourly rate set", "ID and Right to Work verification both approved") in a "what's missing" card.
  • When eligible (no blockers), shows a green confirmation card with a "Force complete onboarding" button. Clicking POSTs to /admin/users/{user}/force-complete-onboarding.
  • Refuses to force-complete when blockers remain — admins should fix the underlying data first, and the action will report the blockers as a flash error.

User-facing UX

Cleaner dashboard — any cleaner with is_onboarding_complete = false now sees a clear amber banner "Your profile is not visible in search yet — finish setting up your profile so clients can find you" with a direct link to Step 5. Auto-complete fires on the same load, so eligible cleaners self-resolve and the banner disappears.

What I deliberately did not do

  • No middleware-level routing on onboarding_step. That field was unreliable as a gate (regressing on every edit) and I didn't want to make it load-bearing in this PR. Now it's advance-only, so it's a sound foundation if you want to add step-aware routing later.
  • No widening of searchable() to derive completeness from data. Single source of truth (onboarding_completed_at IS NOT NULL) is correct; we just made sure that field gets stamped reliably.
  • No bulk backfill command. Production count of stranded users was 1 (Betty, already manually patched via tinker before this PR). The auto-complete on dashboard load will catch any others if they exist or appear later.

Test plan

100 new + updated tests across 8 files, all passing. Coverage:

  • MaybeCompleteOnboardingTest — full matrix: complete-eligible (non-company + company), already-complete (idempotent), each blocker class, no-profile case.
  • Admin/VerificationControllerTest — approving the last needed doc auto-completes; approving when other blockers exist does not.
  • Admin/UserControllerTest — diagnostics fields appear on the show payload (eligible + ineligible cases); force-complete endpoint succeeds for eligible cleaners, refuses when blockers remain, idempotent on already-complete, and rejects non-cleaners.
  • Cleaner/DashboardControllerTest — dashboard load auto-completes eligible stranded cleaner; doesn't auto-complete when blockers remain.
  • Cleaner/OnboardingStep{One,Two,Three,Four}Test — re-saving any step after reaching a later one does not regress onboarding_step.
  • Full Cleaner + Admin suites green (301 passing) — no regressions in adjacent surfaces.
  • Manual: as admin, view Betty's user page after deploy and confirm diagnostics panel renders correctly with her existing complete state.
  • Manual: trigger an admin verification approval on a stranded cleaner in staging and confirm the dashboard auto-completion path.
+729
additions
-21
deletions
20
files changed