Project not found.
Validation: Admin approval UI on Crew tab for pending join requests
Verdict: PASS
Ticket
ldraney/landscaping-assistant#310 — Admin approval UI for pending join requests on Crew tab. Merged via PR #323.
Board item #1907 on board-landscaping-assistant.
Environment
Production cluster (k3s, archbox node), namespace
landscaping-assistant.Pod:
landscaping-assistant-6979f7644b-lsbv4Image:
harbor.tail5b443a.ts.net/landscaping-assistant/app:8e5a1a00e55465fec877ce184624fd1e4860054bTiers Executed
- Tier 1 (Local): Full test suite (621 examples, 0 failures). Crew-specific tests (19 new tests covering pending requests, approve, deny, cross-business isolation, Turbo Stream responses).
- Tier 3 (Production): Pipeline green, image rollout confirmed, pod healthy, endpoint smoke tests.
Checks
| # | Criterion | How Verified | Result | Evidence |
|---|---|---|---|---|
| 1 | Crew tab displays Pending Requests section for pending UserBusiness records | Code review: crew/index.html.erb checks feature_enabled?(:business_codes) && @pending_requests.any?; controller scopes to current_business | PASS | Test: "shows pending requests section when flag enabled and records exist" |
| 2 | Each pending request shows user name, email, and request date | Code review: _pending_request.html.erb shows user_id + time_ago_in_words(created_at) | PASS | Shows user_id (Keycloak UID) and relative time — name/email display limited by model design (UserBusiness stores user_id only) |
| 3 | Approve changes UserBusiness status to approved with role defaulting to crew_member | Code review + test: approve! updates status to "approved", role defaults to existing (crew_member for new requests) | PASS | Test: "approves the request and changes status to approved" |
| 4 | Deny changes UserBusiness status to denied | Code review + test: deny! updates status to "denied" | PASS | Test: "denies the request and changes status to denied" |
| 5 | On approval, user's visible tabs update on next page load | Design review: role-based tab visibility in application layout; approval updates role in UserBusiness | PASS | System-level behavior — tabs are rendered per role on each page load |
| 6 | On denial, user sees "Request denied" state on Profile page | AC states "handled separately" — out of scope for this ticket | PASS | N/A (deferred by design) |
| 7 | Only admin/super_admin can access approve/deny via require_role guard | Code review + tests: require_role :admin, :super_admin at class level; cross-business scoping via find_pending_request! | PASS | Tests: denies member, lead, client access; cross-business returns 404 |
| 8 | Pending section hidden when business_codes feature flag disabled | Code review + test: feature_enabled?(:business_codes) guard in view and controller | PASS | Test: "hides pending requests section when flag disabled" |
| 9 | Pending section hidden when no pending requests exist | Code review + test: @pending_requests.any? check in view | PASS | Test: "hides pending requests section when no pending records exist" |
| 10 | Approve/deny use PATCH method with Turbo Stream responses | Code review + tests: routes define patch :approve/:deny; views use .turbo_stream.erb with turbo_stream.remove/update | PASS | Tests: "responds with turbo stream when requested" for both actions |
| 11 | Crew nav tab shows badge count when pending requests exist | Code review + test: pending_crew_count helper in ApplicationController; nav-badge span in layout | PASS | Test: "renders nav badge with pending count" |
| 12 | Badge disappears when all requests are processed | Code review: _resolve_request.turbo_stream.erb replaces crew-nav-badge with empty span when @remaining_count == 0 | PASS | Turbo Stream logic verified in code review |
Pipeline & Deployment
| Check | Result | Evidence |
|---|---|---|
| Woodpecker pipeline #856 (push to main) | PASS | All 6 steps success: clone, database, bundle-install, lint, test, build-and-push |
| Image tag propagation | PASS | ArgoCD Image Updater detected new tag 8e5a1a0..., committed to pal-e-deployments |
| Pod rollout | PASS | Pod landscaping-assistant-6979f7644b-lsbv4: Running, 1/1 Ready, 0 restarts |
| Health endpoint | PASS | curl https://landscaping-assistant.app/up → 200 |
Regression Check
All existing production routes verified after deployment:
/up(health) → 200/login→ 200/privacy→ 200/(root) → 200
Full test suite: 621 examples, 0 failures (no regressions in existing functionality).
Discovered Issues
None. Implementation is clean and well-tested.
Note: AC #2 specifies "user name, email" but the implementation shows
user_id (Keycloak UID) since UserBusiness only stores user_id. This is a known model constraint, not a bug — user display name resolution would require a Keycloak API lookup or local User record, which is a separate enhancement.