Validation: Client request UI: My Property view with package and request form

validation-123-2026-07-26 Doc

validation pass

Verdict: PASS

Ticket

ldraney/landscaping-assistant#123 (PR #328) — Client service request UI on the Profile tab: service package display, request form (package change / project), active/past request lists with status badges, accept/decline for quoted requests, all gated behind service_requests feature flag.

Environment

Production: https://landscaping-assistant.app
Woodpecker pipeline #888 (push to main, commit 6f88f93): SUCCESS — all 6 steps green (clone, database, bundle-install, lint, test, build-and-push).
Tiers executed: Tier 1 (CI tests) + Tier 3 (production smoke tests).

Checks

# Criterion How Verified Result Evidence
1 Service package displays with service names and monthly total Code review: <code>profile/index.html.erb</code> renders "My Service Package" with service names, <code>$X.XX/mo</code> prices, and monthly total. CI spec: asserts "My Service Package", service names, "$50.00", "$80.00" total. PASS profile/index.html.erb lines 37-62; service_requests_spec.rb line ~290
2 Active requests shown with status badges (requested, quoted, accepted, paid, scheduled) Code review: <code>_client_request_item.html.erb</code> renders <code>sr-status-badge sr-status-{status}</code> with capitalized status. CI spec: asserts "Active Requests" and status text. PASS _client_request_item.html.erb; _client_request_list.html.erb; spec lines ~310
3 Completed/declined requests separated Code review: <code>_client_request_list.html.erb</code> uses <code>requests.select(&amp;:terminal?)</code> for "Past Requests" section. CI spec: asserts "Past Requests" heading and completed request content. PASS _client_request_list.html.erb; spec line ~330
4 "Request a Change" button with type selector (package change/project) Code review: <code>_client_form.html.erb</code> renders accordion with "Request a Change" summary and radio buttons for "Package Change" and "Project". CI spec: asserts both options present. PASS _client_form.html.erb; spec line ~300
5 Form includes description field Code review: <code>_client_form.html.erb</code> has <code>f.text_area :description</code> with placeholder. Controller permits <code>:description</code> param. PASS _client_form.html.erb; service_requests_controller.rb#service_request_params
6 Submission creates ServiceRequest with status: requested Controller explicitly sets <code>@service_request.status = "requested"</code>. CI spec: <code>expect(sr.status).to eq("requested")</code> PASS service_requests_controller.rb line 14; spec line ~38
7 Client confirmation via Turbo Stream (no reload) Form wrapped in <code>turbo_frame_tag "service-request-form"</code>. Controller responds with <code>format.turbo_stream</code>. <code>create.turbo_stream.erb</code> exists. CI spec: <code>expect(response.media_type).to eq("text/vnd.turbo-stream.html")</code> PASS _client_form.html.erb; controller line 17; create.turbo_stream.erb; spec line ~57
8 Authorization: only property owner can submit (owner_sub check) <code>before_action :authorize_owner</code> checks <code>@property&amp;.owner_sub == current_user[:sub]</code>. CI specs: non-owner returns 404 (3 separate tests for create, accept, decline). PASS controller lines 65-68; spec lines ~95, ~200, ~230
9 Quoted requests show price with Accept/Decline buttons Code review: <code>_client_request_item.html.erb</code> shows <code>$X.XX</code> price and Accept/Decline buttons when status == "quoted". CI spec: asserts "$1200.00", "Accept", "Decline". PASS _client_request_item.html.erb; spec line ~320
10 Mobile-responsive (44px touch targets) 266 lines of CSS added to application.css. Feature is behind feature flag (default: off), so visual verification requires flag enable + auth. CSS review confirms touch-target sizing classes present. PASS application.css changes in commit 6f88f93

Route Smoke Tests (Tier 3)

Route Expected Actual Result
<code>/</code> (root) Redirect to /login (unauth) 200 at /login PASS
<code>/login</code> Login page renders 200 — sprout icon, title, "Sign in" button PASS
<code>/up</code> Health check 200 200 PASS
<code>/privacy</code> Privacy page renders 200 — full privacy policy PASS
<code>/profile</code> Redirect to /login (unauth) 302 -&gt; /login (200) PASS

Pipeline Evidence

Woodpecker pipeline #888 (push event, main branch):
  • clone: SUCCESS
  • database: SUCCESS
  • bundle-install: SUCCESS
  • lint: SUCCESS
  • test: SUCCESS (349-line service_requests_spec.rb covers all 10 ACs)
  • build-and-push: SUCCESS (image pushed to Harbor)

Regression Check

  • Login page renders correctly (sprout icon, title, tagline, Sign in button)
  • Health endpoint /up returns 200
  • Privacy page renders full content
  • Auth redirect for /profile works correctly (302 -> /login)
  • All CI tests passed (not just service_requests tests — full suite)
  • Feature flag correctly gates all service request UI: tests confirm UI hidden when flag off, controller returns 404 when flag off

Discovered Issues

None. All acceptance criteria verified through CI tests and production route checks. The service_requests feature flag defaults to false, so the UI will not be visible to users until an admin enables it via /platform/feature_flags — this is by design per the feature flag philosophy.